Merge "Update prettyplease to 0.2.29" into main
diff --git a/crates/googletest/.android-checksum.json b/crates/googletest/.android-checksum.json
index 2db3741..774a9d6 100644
--- a/crates/googletest/.android-checksum.json
+++ b/crates/googletest/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"d6b2b0d30a03b5a480e0c9a54b655d8eb406a699a4d54a1b79f086264f813155","Android.bp":"26dbfa23b3d723fa6e4cff6247a4574d3de9e15abf7ea529443ea6279f723c08","Cargo.toml":"a5ea2564b9871a41486fa1e377e576e5f1f3f173de5812f14ac7cd6f0eff3dce","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","METADATA":"699e61a9efcc1e3a7534f11530d9486333cc82b7bbe820d66aba3d6fc4d41a6e","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"07d4f57eac87d9661107bd8ae50f13f05e63ab60a22a5d6e84159a446412f658","cargo_embargo.json":"2b7db5752ae086bd42bc615742f3fe1183fff050dd0fe67aea0c4519d4b592d3","crate_docs.md":"22353450101e1ea34aebd2263b5c9f6b0fc2dca5d876d2a755b52fa9c0a570b0","src/assertions.rs":"dd45066e3462691b73cf78ea08d86bc6106c9d8b3292ea67de80f7c65d5425ab","src/description.rs":"f342abc02a9808cd6ac8efbb0dc5653cf75960fe0e6b65c604a02eb33d18f078","src/internal/description_renderer.rs":"ad2cfa43baa63ec7c33b73cfb66f16fe7fad6379576bd896ab22a1ec418255f0","src/internal/mod.rs":"13cf778b7e517506ee21cce6c347c409ff476a805b76874a320e350a7f5f86de","src/internal/source_location.rs":"2a0bfa8a6556301f4b469711568741d601f3cc35bd46efa48887d123e1ad6cf4","src/internal/test_outcome.rs":"bd48ed72e39f7cb48ab8591ee0786d4aa810a49877e4a300a949dde27fc28f00","src/lib.rs":"e79ac41262f17c185c57c6993ac2fd3aad1f2c127f160451fdfe40f9c2dd6570","src/matcher.rs":"6c4f8bb7c1869c5a4e4bfe4a6fa88ea253256fceacd7d11a71299f17b044a4fb","src/matcher_support/count_elements.rs":"7487aa09ddfd68e7786943bc68ba97fb54857441e9fb7160f5eac1785a1d2176","src/matcher_support/edit_distance.rs":"0cf896f82ccaed727c0a1c7e2578fd1430d812fe52168e17b08e53b185385c81","src/matcher_support/mod.rs":"9e594fb5d2ae79a34c451f21beb1912cf9c05646156a8bcb482dccf55adece03","src/matcher_support/summarize_diff.rs":"7fedfdc6228307cce6da7ff3519be8d2c516c9e2f5fbdbc05ce679e476877024","src/matcher_support/zipped_iterator.rs":"2c022b163bb1defda1522df9ff6824c0db0044e9314cdd45dfe39e2e1e335b96","src/matchers/all_matcher.rs":"f58ad8690c1237eb53c7d745ef9df667f6c38dc19e02d1a0641d563392feb6f3","src/matchers/any_matcher.rs":"d5c4d03d9f515bed370ca538013b6d6d8d62c57309fef9decd3c4b684a73e4a9","src/matchers/anything_matcher.rs":"664c706c76d7603664a81f043589f5ab6cf1e9585bc307ab07a16df4d75cad32","src/matchers/char_count_matcher.rs":"2707f530252e29c45b953e4d5fb9c86e0ff62041b07ce6331cb9cf7db9858e2a","src/matchers/conjunction_matcher.rs":"8cb736a15d2be8405e200d6599e2c6d8e86b79c27822a3bde2361fdd3c9d6a2d","src/matchers/container_eq_matcher.rs":"97eded933a75fc0a42905c455c53a31bea88b5f65cb7a072214313db72c97d84","src/matchers/contains_matcher.rs":"297cdbcf152caca7593f809fb805bedc25c5448157f351c63cbaa97e10304c20","src/matchers/contains_regex_matcher.rs":"d818d0aac0302fb9aaa805aa72cbec240c1303b59e59b85a070a983796a068aa","src/matchers/disjunction_matcher.rs":"a0e72b4c6aaf33c468f74ef9d276006e800ad675a8e572c9310271a4cb2b0d0e","src/matchers/display_matcher.rs":"2a73d326a2e547abbebc58c401128649187ec57aafaf3a71cde58ec0611646b7","src/matchers/each_matcher.rs":"b0e0d70598601135f31715823cbeb2c1709900b11d07c1d9305e37f31adff61a","src/matchers/elements_are_matcher.rs":"16e10c4bf6755c26ec72f6343aa1b363c8260dd387561c9ab2595e3dec84b46e","src/matchers/empty_matcher.rs":"6d29920215cb48e880a1fa0519727467e83ab74cccd3472b9aa2bae016b7d35c","src/matchers/eq_deref_of_matcher.rs":"9d1fe8c2094a3649ace379731af601b6c4abb1aed412a718d25dcd191f1c9496","src/matchers/eq_matcher.rs":"e9b985a9cc3bbd471aead0cd7100b0725edd4faea0d35713f43385ee19ca3af7","src/matchers/err_matcher.rs":"0da1c872fded683fd515786c5b8aab8ee99c3bd8d8ba35ae74ac354473393447","src/matchers/field_matcher.rs":"50b4589bac6222e3e7a3aa1c140fda70a259fde39b538b7513e3305362c675e3","src/matchers/ge_matcher.rs":"af4641c64a43fa29c9d3878443a62dca64620c311e1957609277ea707fcf5a7a","src/matchers/gt_matcher.rs":"d9bf9d216f61335e067a39f421ef1e3baff2871776f6287541f06711c23a2733","src/matchers/has_entry_matcher.rs":"f2ec364e57853468c941f52329dab2251b581a7c2deca69e953b4b94be509e63","src/matchers/is_encoded_string_matcher.rs":"f95f3fed1b0e161829983fc50077dd931f5cc16e7fedfec2de088a7083d926c4","src/matchers/is_matcher.rs":"6a2a68665a079e21406da1454408d606e62c5e21ee07ae94e7eeb4335ea3f044","src/matchers/is_nan_matcher.rs":"33a179908c1f0ab95351c6ec623d5d0dc494bfb64cd8df2ff4244f01916395e7","src/matchers/le_matcher.rs":"c2c6818514773564ac578ffe86e5e2b9bff58785ddab0b37697c4bb80bea38dd","src/matchers/len_matcher.rs":"015b86c053595e3fbc7ede581ab03b5e168369b6f8cd133f3b479c181f474001","src/matchers/lt_matcher.rs":"c937c756416f7ba5f5ea907f9ae5539ad9520c33227346c4abd481c8e70330f1","src/matchers/matches_pattern.rs":"f17968108e733459a06fbb0709ed7b76ce8245e183925c06d8575545f5c23071","src/matchers/matches_regex_matcher.rs":"3d95e01bdb64b1081cfccb11465edb94dfde8566ea3950d03fefb75eab854189","src/matchers/mod.rs":"09235b20fb2166d2d5c72316a9b3ecb736344b7a9636855bd9d94bb1fb5b4831","src/matchers/near_matcher.rs":"645e70df5f2f4c1d3e3b89d377296e21a30cb90d34426069f5aae36fe29206cb","src/matchers/none_matcher.rs":"d1a13d3de32e7ba5781accf9d01104cc54d0a9e6df3a38cd00768ea2f6eeb18e","src/matchers/not_matcher.rs":"43ec462558eec8021ef238a72541433210fe9915be235a5845cbc9217e8f0f23","src/matchers/ok_matcher.rs":"f358885ba1be7f8b24ad658788c9c1673f03950fda6b166d2eecd8eb1899efce","src/matchers/points_to_matcher.rs":"30f88451c8806aee81d0f9925583f15531773399089e54ad2fe4b81544b45fb8","src/matchers/pointwise_matcher.rs":"8dd05061cb3a86cde777c16f4b9d84dab6469084e8878dabfe4f34748555a96d","src/matchers/predicate_matcher.rs":"e18e614fdb2a4ce28ae92b33fd605641b9c9720391e0e9d067f90b7060b62958","src/matchers/property_matcher.rs":"8507ec831853e9947f57fece53bf7e79b1c902290ae1cf55284186b8f06b354b","src/matchers/some_matcher.rs":"c643fe7aae9fac6565894b83080a352c3f843c785e44e27ad3053eccee141403","src/matchers/str_matcher.rs":"37657e56b32530124d04903ec7622a35881cbb39fed0b719a24c1362b2ab8574","src/matchers/subset_of_matcher.rs":"1bf4fee1617e65744b3ec9cc18721783804ed84fd27d7d7f253b5fc82979b6bc","src/matchers/superset_of_matcher.rs":"d1a8dbd21113d22eb1fef9fcf45d136a388a6ce4622f8244344b0b81d2856144","src/matchers/tuple_matcher.rs":"9e08b6b4e7bb5cce0f47047ca89a34d058d634eeeb13357c3c8140962f20a1cd","src/matchers/unordered_elements_are_matcher.rs":"802cad45924cdfcf1d1a72b83aa01a0c06d7e6b01a219f76638af3cacae69056","tests/all_matcher_test.rs":"91a95c6aca516f2cedf7ecb297280ae52b5cf96e25eafe29cd0b4bf40493b5be","tests/any_matcher_test.rs":"e8aae6ac19e3a8501cd0101cda2cc0c5fd49ce05bf81651cec925e2f01eaedfb","tests/colorized_diff_test.rs":"9632e5d8460f67331df2edda7848db3b79bcefa043cd44d8aab7eafc90945dae","tests/composition_test.rs":"dfe726b38d357ff8d547eeddbdca539b8bf70f6681125f063c88db6140c8bf65","tests/elements_are_matcher_test.rs":"5ccaafb93fec5d9790edefb2d06ed5376f898e8e971d9663614bffcc939a96e0","tests/field_matcher_test.rs":"b3e7f0f6d403d4dc768d282128ae6eda68a96fcf1cd3127c107cf68f423a0bf5","tests/lib.rs":"d48d840a933a9a9e1076865eb6e443b538d4f37d94b45b7110333511f9a50d9d","tests/matches_pattern_test.rs":"35101434bbb3dea2e3054dfc55381a886d2055002156ebb2fa1575fef6386a63","tests/no_color_test.rs":"13795788bd9045f25f040067cb359e4d55505ddf6a1524922db23f3aeff570af","tests/pointwise_matcher_test.rs":"e0c14a1e030f83c3ebfae00d73c49af60d5958836aa8264ce4a3374330196f5f","tests/property_matcher_test.rs":"57da661e975d2af939ffd3bbd5fac7d11b2d344fa62d709a78080a68053370f4","tests/proptest_integration_test.rs":"0f75081bd6bfd89b8a5e430238a9da78a2f13ce6ccc4e105e32705596e9c3a7f","tests/tuple_matcher_test.rs":"b16ce00f9857206f9ebaf18175c4d2df5de739fe85873fb0f30b1c69e794ffe3","tests/unordered_elements_are_matcher_test.rs":"2d3402327bf2915cc79a3fce01f939e4454d45dc8f53826211add79d389f3c29"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"410493270e72d4760c1e835f510b65d50a78811d6946a3f93170a161f8b9d815","Android.bp":"31cf3dbb287b6b5c5fd4dd46be662c4b64ca88577b495dc9436f8d5db5707bb0","Cargo.toml":"a194180b19517aeb9c05048610d5f7422c6e50113ebc55b5f381f06b22414b3d","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","METADATA":"8e573ebeca353ef9a3892bf4ba8ae4c0b0f781d737b2716ce2b791903a85540d","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"440fe5145aa23165197219e0378cbb71cd7ca336fb1dc0fe53a3d5a2e3d4471d","cargo_embargo.json":"2b7db5752ae086bd42bc615742f3fe1183fff050dd0fe67aea0c4519d4b592d3","crate_docs.md":"86974c308ad2e035d0368179b32191c32672353273cfe5ea970687a97849b016","src/assertions.rs":"3f9defd623a7006b23d3963caffdf701b78c8cf0696a94ead1541e43698d3866","src/description.rs":"7bf3fec806182d8a011b46ca0dd7f14adcd17ece7fe81dc7770a232ecb21b21f","src/fixtures.rs":"b8ebce97930de9822eed6fa6e1d0aadf9e8eda61c989ba27644878444babe143","src/fmt.rs":"f6e89ce1eac6215301c185762f0ca5214efeda1fb431a9adfa29dafec6bc238e","src/internal/description_renderer.rs":"22f0dafd9d02a37483bbd689409f01b001184d30c095459b21c61cf90df071dd","src/internal/mod.rs":"bfaca36e81794979c6f7976e96a6ac8f7b09d0181ac8f44538d40a2e40d8453d","src/internal/test_outcome.rs":"e8a39aa3bc29d79e64ad42da4f22a18452b76171d28d14be3cbb0b822f517299","src/lib.rs":"109c5e2b09705cb4bd11330e10c69daf2523b01422f103db6c10353823f6fccb","src/matcher.rs":"e80fadb4a9939d190e1fe94570e9f50ed2359842505d7862e34136cd8f6ac55d","src/matcher_support/auto_eq.rs":"5a6d9607a884b9a21e0ec29a8b75a78e741448ac858b618506521eb67a2d255f","src/matcher_support/count_elements.rs":"0973e4f0f73ea1d92b8cf6559aa696d84ae27179765f93fa03acfbd239a447ca","src/matcher_support/edit_distance.rs":"83aadc058939cf5338038650b9edb85fdf2ecf6d90ac35656eda69d7f4715102","src/matcher_support/mod.rs":"fc213e3f665b23bf34f639dae133920fd17db0a5c673d4957f9cbb3d4e706d71","src/matcher_support/summarize_diff.rs":"6aafcc3b42872ce9a07e0d422816f4f9e62c470b028e36bcd577c140d5017888","src/matcher_support/zipped_iterator.rs":"2c022b163bb1defda1522df9ff6824c0db0044e9314cdd45dfe39e2e1e335b96","src/matchers/all_matcher.rs":"b9534e5d1657791b341b411989b5ceba03a48ad625afc208a4290c04a5a3c3a7","src/matchers/any_matcher.rs":"1b082cd64719aad9f01b3862ff87b20cb41ccd64c268f18ab67e9a5207f6ed0c","src/matchers/anything_matcher.rs":"1bcde76add015ff9edb69b6ea55eafd8d3e34b61f56efd146fad748652d974f3","src/matchers/bool_matcher.rs":"abe9fd9f0afa6b0276e62e45d19e6c030a3e9fe4e162506ad77a3a815062d3d0","src/matchers/char_count_matcher.rs":"93ffefefa301e1968cd0cb166e451fba1662d684a682f66229cebb3c01e78c16","src/matchers/conjunction_matcher.rs":"15761c0a4cbb2f97935088f58fc00b659f1a2c12f36a22958ac96c203e16e5f1","src/matchers/container_eq_matcher.rs":"03efddf8e882415f9734721c6fb4b507f60d376088594be98c684cb913caca0a","src/matchers/contains_matcher.rs":"012416f5634b0d1c72fb529983fc9a7d42f70d652bc786402853ec40f1675845","src/matchers/contains_regex_matcher.rs":"04aa3ec505c57ed5cebbcbd5ae8341d3e45ed0dd567ba401dc8e63f1c65aab8d","src/matchers/derefs_to_matcher.rs":"3d2f6870fcdc52c4619914a04dcf4d4bf94e28f23287f06091088e4a00512670","src/matchers/disjunction_matcher.rs":"193b85d9ba177af4592e40be36cc67bdb4d2d4ce561d9f9bdc8f8e196a11cc05","src/matchers/display_matcher.rs":"f6b7216ec2c6f6200c4493e874d73c19a921b65381f9dbf147c689454ed5c87d","src/matchers/each_matcher.rs":"5cd5d03983475db8c93ef89781e3ab3e68fad81fefd829eeedc96be94bbae4e9","src/matchers/elements_are_matcher.rs":"6e593c19c7ae1c60f5e8a811ac2a5a48edd21d72e788af69fe588fbfd6caf110","src/matchers/empty_matcher.rs":"e5dc9e0e7b2799b14590b2d93ffee056339b0befa9acaf4a17aa15799e8d5716","src/matchers/eq_matcher.rs":"59f3839dc3ba5928c784fbc72c79d90d41a23ac14b27de6d8f3d50eaa2cec866","src/matchers/err_matcher.rs":"eef9b9bdbe342903c4545d7696e52d7b06df6677a91a59274fc16999ee5f658e","src/matchers/field_matcher.rs":"db9c32306aad1010dfe9c6acc96acf867a22ff8324bf560ace44f680e07d174b","src/matchers/ge_matcher.rs":"6fb61cbfb8f3e81f71900df9b0420ae155c0a19591cc95ecb024481d9047299f","src/matchers/gt_matcher.rs":"9555f0c294def104eb738de55c63f012267375e4db269cf5ca60ff5ea8d56275","src/matchers/has_entry_matcher.rs":"20bf4c271ff1d3673393d6f8c00ccce4d1e0f1604a16055f0c46bc0e5b793ddd","src/matchers/is_encoded_string_matcher.rs":"f4b60009696d1396029d36ae187fcc3528a661e1b5c38479fac9f4e9e416dd4a","src/matchers/is_matcher.rs":"df8fc55cec747b9764bae05595a403dac253514a21fccd98ea7c46a83e541999","src/matchers/is_nan_matcher.rs":"111cf9e8aca5d709942909e41fa0f6578418bc890dfeba52f9102038c5633283","src/matchers/le_matcher.rs":"3e151e3d13f944d81620aca4311dd15b382dc575266638df09eeb0e66cc674e0","src/matchers/len_matcher.rs":"24adda42e1750d8bb49ffe12a3f431c6c173c27c313d973b7eea31dd616a1642","src/matchers/lt_matcher.rs":"ff7ce1397b713955d643debc6200dc0bd5bfc4f8252433ea1650f105886ae8ef","src/matchers/matches_pattern.rs":"5c36c3f0eecded409b7fb336013a84db00176f861b65f07dc82f207570feadba","src/matchers/matches_regex_matcher.rs":"ebd8e88cfeedf394bc2550137d67dc0e15759565d49f4e3ce2e7f59c9f24808d","src/matchers/mod.rs":"fa62f807cc57cc5ee784f630e75b7447079ff5ae7513e2f15086dd061428f2ad","src/matchers/near_matcher.rs":"1e009423fc9326a984558db4efaee53d5de4acb820ef475c72c4696ed074a99b","src/matchers/none_matcher.rs":"fa403c8c7868fb41005042dfd874d5d096c52590bdd252ec5be990225f36b6ff","src/matchers/not_matcher.rs":"c98ce0362a9d7ca308b3bed4b7853d7320b22478185d777cc09fd0c627010ea0","src/matchers/ok_matcher.rs":"cadab709d5c52f2219bf59118a0a29dba8614532ead56847490e489634e5d147","src/matchers/points_to_matcher.rs":"7e845a3fa699be23e917dd90bafe4d68bbdc8d7c3fc042ba689277e3f7c8ec4a","src/matchers/pointwise_matcher.rs":"ecccadc3baa9afeacc6242e41501b27cb1581dd1502aeb127ff1fbf52cc62338","src/matchers/predicate_matcher.rs":"b9d64d86e7ef292d57aa2a62bab7ccc64b0fec262fe0bd73d08924e203d784f5","src/matchers/property_matcher.rs":"1f4fda841361381145c2c96c56cc2d50f4af008c169bfec6615b77e4b6886722","src/matchers/result_of_matcher.rs":"0d0f66f4f5d4e76062e0d427e6f7349cceb7a574d3097eb5ac982f16c311f848","src/matchers/some_matcher.rs":"6179779b798ee60e4fca6ded4330206a398798656f3a6ecb00f44cb7e81d39d0","src/matchers/str_matcher.rs":"1aedc67db3a0d0197a32f52f583e40bc89a95cca1dd70513fadecc24a7733346","src/matchers/subset_of_matcher.rs":"1a463f7c277f31e7f07fb6ef3901ad4e6d3907cb8725f2d8ec4647a4e5a8ffd8","src/matchers/superset_of_matcher.rs":"5b40ed602c8aabd66136f932aac1f5c031b99e1d3c0adcf81a50b998e1bcfde7","src/matchers/tuple_matcher.rs":"6dc905a4ccf5748f855868d2f5819dd3b92d5a4934d2639a2a3e3f6e40050b70","src/matchers/unordered_elements_are_matcher.rs":"1b5508b3aacc164ad9276834579d75baa241504f26d591995c04e51959daad01","tests/all_matcher_test.rs":"a1347ab53409098f3ea78068a2523e2f6885c22d988d50131e32e1c21f96f7a9","tests/any_matcher_test.rs":"709ca1a20c82fcaf6be821d4b328506f17855e5ffbe21996f27d67c3ae96af04","tests/assertions_test.rs":"c6e952cfed6998b6ecc7d4260d7f6c3b3a29f031856e6e4683a223a0cdf392b4","tests/colorized_diff_test.rs":"a55aa6e001df9fdc8684136a59555bf0ec27ae84848aa4c9a3a31842934b30f7","tests/composition_test.rs":"51ff2e05d3b4dd06e246eb9e4cfb3c1997369ca9cbb34ca2a7d1d5599a9900c0","tests/elements_are_matcher_test.rs":"c98dd588a184b118afd71767b3f07dfc8299ad8a9147e0835e33906a43a41552","tests/field_matcher_test.rs":"634fe967a801bc077f477ccb38649580011c2957b0f37e06a73f5654b46a7211","tests/fmt_test.rs":"67c0032bc3b3d2e07aa74c87bc583c85cba477840f1782358776f5aa33c0e3f4","tests/lib.rs":"357bbd54eb3dc3af000e2f3eaecafdc0a59a1fca30445773b64872c050870657","tests/matches_pattern_test.rs":"390038894798d08feed6dd2209427d8729dd9029ab95dd685190bc24de28364a","tests/no_color_test.rs":"f7472284fc864d4654d4677fa44274825f9286f696228c59175ee6525a8a5343","tests/pointwise_matcher_test.rs":"159bc32c50b792e9c96327c970ea44ae895e842a5f92fe817ca4d8f1e344bbc2","tests/property_matcher_test.rs":"6f2afd05c0a55624c3ef6e0f30398cb49188690956351c9a2f2e6f428874f381","tests/proptest_integration_test.rs":"0f75081bd6bfd89b8a5e430238a9da78a2f13ce6ccc4e105e32705596e9c3a7f","tests/tuple_matcher_test.rs":"3cf3a362ec753ba2e0f48db6716f32d31521335ed66a2d27aef69a271f5a902a","tests/unordered_elements_are_matcher_test.rs":"38c4f8711a056284afa0d3af3ada3a216d1b58ace71b3d1d4b844277fca45217"}}
\ No newline at end of file
diff --git a/crates/googletest/.cargo-checksum.json b/crates/googletest/.cargo-checksum.json
index 4b4a4f0..9ceb8e9 100644
--- a/crates/googletest/.cargo-checksum.json
+++ b/crates/googletest/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"81f8a9a3e6798d1747bcbdd240617b84f26d5a5c88ab748152f6a7ce792e3ef0","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"15ead96c81651817d7b9d02c697aaedf0909a80dba2d0acb5c173c9d1d98d3e3","crate_docs.md":"4fbb126bcf704aec6e587348c5683030f28516baf1924e37f0eb4908e09a79ee","src/assertions.rs":"6bc4c890ce36a54838049eb39b35807009fc103ed2f07980e76606c8f0a417b3","src/description.rs":"b51af6bfe83fe8d41837fea2d6a8eb9efd0966df8384228aa86cccd444fd688b","src/internal/description_renderer.rs":"df583f7096cacd4e63eb720e18bf17c7f5e45eb81c57bfcffc04711abce5add8","src/internal/mod.rs":"bb5bb3dd31ccd33f893c79f81e4f629186d1047935036d773a2d5cee3feee959","src/internal/source_location.rs":"ae5014cfea179d3a619714a64810bb8feeb7e8c9267cc8c9066cc52620762b46","src/internal/test_outcome.rs":"9081c258ba3bad2f1222a006a0cfc6268b6c06fdd998e8d51f1ee6c2079900e1","src/lib.rs":"523c79fb0da7a59b0146456c919db2c20cfddda5bb03c7494fb70dd454d67390","src/matcher.rs":"6eaeec6b6d90b4ae74d298a861c03e985147f194c0c6ac296948b479d4721414","src/matcher_support/count_elements.rs":"9e9b1c238c84fb65319981267842c794f406a696895cfd84da463e050e768c23","src/matcher_support/edit_distance.rs":"72c03abebc181da092c40853e7a7e6a9d78a80074faca188bdfd3f80ef5715c2","src/matcher_support/mod.rs":"acdd182e13ecbdaa55f66526bd18ca4be826e3fa2ab9ea593d10d4a867aa12eb","src/matcher_support/summarize_diff.rs":"a27afa050f0e0b38a5813cd109f0a56152ac1552cd7400866de27f4622ba153d","src/matcher_support/zipped_iterator.rs":"ac1a1a54f866f977255403a4f7e7613a4116af163326029c54b65ca1ac57b538","src/matchers/all_matcher.rs":"b3cdaec8ffa2bb842ec8b2a9ae37890f88b00d48708c0d37584293d5d5f7ad36","src/matchers/any_matcher.rs":"c08c104699a7e7b0ad3788c1e786b15626253707de33671908cadef34f8b43e8","src/matchers/anything_matcher.rs":"d0244d3338bb8a6b9c677b1c5bcb56b56d6736f762b68110e14317f3b67c273f","src/matchers/char_count_matcher.rs":"eb89c497c73b7ef94d2cc433da0f1bdb6fd97e79fb137e7b2e3c9fd6758899ba","src/matchers/conjunction_matcher.rs":"fa7bacd6ef0b1b5fd858bb8ab48de82d09a3d4bfba1a314e5c00e35ee858e46a","src/matchers/container_eq_matcher.rs":"9b74345b3e244a574048928a3547ea00ae9dd75622feb3d5a7139c13d2c8e60b","src/matchers/contains_matcher.rs":"418c2da2366ea315bf75e610323faedf2ab6dc0d9d424cd650be691f811a384d","src/matchers/contains_regex_matcher.rs":"d6defb887ef498f4d7a9958a313927375aed996d78629e7213dffe07e2719b1c","src/matchers/disjunction_matcher.rs":"18d1918f30d58c19a9991caa0ec0cb43f55d196824373e80735a7c50ef6781c8","src/matchers/display_matcher.rs":"6ab9ddcb28173f5959d5c82dea9cb9f779f260cae2995e5f318972775317dacd","src/matchers/each_matcher.rs":"218eeb263cb1683c6a20ce80d0f127a1659a6f5da3f74bb1e3d810ab259b80c9","src/matchers/elements_are_matcher.rs":"55c890a72026eb8bc717bb0d5039b98e7f8be09da1a3c46b5a7505f63a76bb2f","src/matchers/empty_matcher.rs":"a6accac5537e29f313356b46d344354ceddd89ab9857a52febc8005b0fb5fb21","src/matchers/eq_deref_of_matcher.rs":"762038bd3e8912fe064504ca173b988bb65e3792b9d935f086cbcfba67ce346f","src/matchers/eq_matcher.rs":"fa03d1a053c6d5b0bdecfc9515e354037d9fef64cd6d2c96dc04d2ef7eb64d71","src/matchers/err_matcher.rs":"4aef1b1a5fc33825d39e4ff052dfeef7996d02eda5f931c59114125ec40ebc62","src/matchers/field_matcher.rs":"f18cc28b683beb179bbf3956ffc86852402fae96ff3fa0936ba89ba8c5f7ea65","src/matchers/ge_matcher.rs":"4c67bbdb9e4acb95f3b2b38dec4d6793781330eeb0895f3edb305215d2bcf4a1","src/matchers/gt_matcher.rs":"d557a138e30b47c9cc65d26739df3d9698ad180898cabbfa18d9a0db6474c53e","src/matchers/has_entry_matcher.rs":"a39375c7a7a12daf5080980a07564f4eaf39abe143d2bc40f64f5942b89223da","src/matchers/is_encoded_string_matcher.rs":"91f1c2e89377722248346c9cb3e87cec96e9bdec66f1a391332263ec86f955cf","src/matchers/is_matcher.rs":"6026f6f7dc9c08e65c498cbd73ea47613e65ed3e13a8dcaa0b303c54ef4bbf3a","src/matchers/is_nan_matcher.rs":"4c0d2b507e9a61ccdaaa342698cf48ed31e3d62a14b2eaa07afb95de4afe074f","src/matchers/le_matcher.rs":"6a168eca823bac8e7bb82f9d5392774fd1e0d9616cedd43c67fcdda46250a964","src/matchers/len_matcher.rs":"b77b7a4685e81ff79fceea5f382d9274e2cb8bae2abaf4f2f69ca6af4a0ceb00","src/matchers/lt_matcher.rs":"12535f0c0edad7098869fa343c225aaeac8417d97c665ff58ae917fe8497c5fe","src/matchers/matches_pattern.rs":"7d655dd989035ecdadf6cd8d59f3057bc1f115ffb162517423d6588c56ff20a8","src/matchers/matches_regex_matcher.rs":"edb52ef557ced3af81f38908917417eb08c917c166bf6fe59ab8223b53de0050","src/matchers/mod.rs":"1213c95b199519d0b978198cbfbe3239d02ade145cc6ab18a5bb91c7141b4fc1","src/matchers/near_matcher.rs":"127cfe0cc9157079a28025dd0ceceb7dea8329566e46995f4b8d1de19bc92a5e","src/matchers/none_matcher.rs":"2d9908719fcc59e281207e69c3528c8133380661b361bd159cb25e28917789ca","src/matchers/not_matcher.rs":"bf2057c6cf7b21964de766ea16d8902e6f0174793ecb602c26d39cdae5761e8e","src/matchers/ok_matcher.rs":"a05c8eb9addfb3af57a9b0b8fd56f4a966ce927be5be265b8978e19c240e145b","src/matchers/points_to_matcher.rs":"5cf3f776756b904d66847ddd49b455681266ae32c37de7eb3de729a66e1055a9","src/matchers/pointwise_matcher.rs":"0d8f9ddeacc13dcc6f34b0a4fdb7c0a5ff54f3b4b5e85f061c7907b9a2390f9b","src/matchers/predicate_matcher.rs":"d23b67acecbdd5a3bb410c8444f376947ceada2c45fd2d5e775c2e6cdcab407d","src/matchers/property_matcher.rs":"21ac721ad28ed02bc21c7d86b8fde371e1dc0606c5449b6e9aeb2c20cb0aa47f","src/matchers/some_matcher.rs":"654478b0966b9c39d5b07367b595925065cce2580c8bfec0789d5d01eb44fa41","src/matchers/str_matcher.rs":"9e24f276cc060d5174a78b862ceba65452669810cbb64505382c13395e289c10","src/matchers/subset_of_matcher.rs":"6c5f875784ae05d26adfb7532ee5d089d3de6e6369abca6b1a84f6c4aa5977a9","src/matchers/superset_of_matcher.rs":"05e6b4f965f033071fb5feec2e0d93058461b273d4613f338bd4124d59635cb7","src/matchers/tuple_matcher.rs":"d028f7e2b54264256eebc05f54891798f769d06be72572672292d77c2b6a2726","src/matchers/unordered_elements_are_matcher.rs":"ad0cc9f229fd97d689fce3b93f809260f568487d1ba17c4c8c4aa0640ede9000","tests/all_matcher_test.rs":"4b0690e97dfb426f336266bb2b09f19dd3f86504f400e89f5821a888a71e41d1","tests/any_matcher_test.rs":"064343665458ee48502697a46b14059b1971f972bcb9e92db09638b5f053505d","tests/colorized_diff_test.rs":"39227cb13b2914c14231a998bba4243309645e8f029c3c62f2a2969d699ab9a7","tests/composition_test.rs":"729dd849a7a423a000324d308874aa89558b0be6027bf4f608224a63a90322b2","tests/elements_are_matcher_test.rs":"9b2926f2f6efcd0a96f13f1f370e040c7307b2da3006f3c68a160776c5057729","tests/field_matcher_test.rs":"bc34936df7c3c4b36e1b9998565c8363967f7eeb5bae0afe4001cc0fbc36f5e9","tests/lib.rs":"506b8b37c82a7ee08ef9e8c29437b98c46097d96ae52bf75ba3c82ac24096996","tests/matches_pattern_test.rs":"8372b8823edb0028f3ed38fc9b669fcc3ad36bfa153e7671e3f6cbc8bd7798da","tests/no_color_test.rs":"3e248b14597b2b6c2b60a52f7457d822b8a19e8f5604704aa0175fd0e21069cc","tests/pointwise_matcher_test.rs":"5078a462bafe11ed27501ba789d73e4e910c2e128197c0069e1aa790cdeb2684","tests/property_matcher_test.rs":"674217ce981e7be12f569fe78279abd89a3accfa81753f58aa535814887bec09","tests/proptest_integration_test.rs":"948f4d59b7f325faa522e53f3c8a4542be349f368cb790a39c157eb6c7b47014","tests/tuple_matcher_test.rs":"2f87305c29a4692f25a481f2a30e79a765579fd538872f57669b48ca576204f6","tests/unordered_elements_are_matcher_test.rs":"1026cefc59daa8fe2aa249b87800238699d2352224c04ac4edc648b7052a13ef"},"package":"ee44a233e2e661240ef77ed5de92ea9ecf70d4832963a8582a6681e1517f3673"}
\ No newline at end of file
+{"files":{"Cargo.toml":"e00cc51a8d859b2a8fd95e199c13db5e16ed79837573b9f445e086e255d43893","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"12cf77cf9d98dab80416f8135448cae5d0bb532e9b69c63b951d49861d8d649a","crate_docs.md":"b25c57c437bd7974921b9f7172e868d3efd3b1551addc8c47f8ff6a4fc5d8ee1","src/assertions.rs":"43883536abf5f4bb9c2a746593ad1f4cba811574838f700daa703f5b952b2c41","src/description.rs":"f9fb18581c36db85a7415ffa5c9f646dfafaa7594a0a4d00ab66729de5a3f3ad","src/fixtures.rs":"528672a95a3535b2f18d28b29c2ed0a9262462738984d94af74d7cda0e4cbb86","src/fmt.rs":"986f1d796eb585999dc0614c5aed3c94230c0ba19893d2b2cf8eec90e20a38c8","src/internal/description_renderer.rs":"c7d685ed7df27e8670ec77ba7265dd220da84056e467fb62e8faf3f5e0898e8b","src/internal/mod.rs":"b07f41cae5b2b78806be89fc48da938fbe41277c37456d79891496a26e2c921f","src/internal/test_outcome.rs":"40f26fdde8a101242d376a4ffa1157fe177a017d0d4659bc747eae93762f83f3","src/lib.rs":"07bb924c81b725635a2df75b0283a3d581a92cf544923a6d6a869fce491cb4b4","src/matcher.rs":"a07acfabdc658cf23df089763c9faec77ab586cc9e153c01178cd0c9853628c6","src/matcher_support/auto_eq.rs":"e7a95ffb7ebe4f23986920e494e89ac254c596c43382bdb730a17eda02b83d87","src/matcher_support/count_elements.rs":"34ac45d3fedef1e9ff9ea46144e450fc2972e17e3d266699c61fdf68cd5ed2b1","src/matcher_support/edit_distance.rs":"e91a22eb46f1000a0ef2efac547ab1a05a18612d1c5024400800a0413ea8c74e","src/matcher_support/mod.rs":"df976626c666df50f9a12ab4486fb64c327ef9b7ee3b497d89e9a7aada889c1e","src/matcher_support/summarize_diff.rs":"82700a07cb45c20d6375227928f32e17422486373bc10377d4110537bfb271a0","src/matcher_support/zipped_iterator.rs":"ac1a1a54f866f977255403a4f7e7613a4116af163326029c54b65ca1ac57b538","src/matchers/all_matcher.rs":"a12ea32a2872ba6e0194c4b294182157ea1fcd8dd8800005c45f9faad81aa6ba","src/matchers/any_matcher.rs":"2d2b1db30790c7dadbd7e1b77e1c796b71e3770480d21e15bce7033eeae0ecb2","src/matchers/anything_matcher.rs":"5905d9ae3e01274ec514cb5f09518dc7bfd416fc0bd57b02ee10d3ead04ce7bf","src/matchers/bool_matcher.rs":"245a72604474071a00f6a4231a00d1c984aca035cc9d79c0aba090eb46d686b1","src/matchers/char_count_matcher.rs":"fe013c27c9e3a7e8b627472c272dc38fa0ab0674186384bc53ce64130821ae38","src/matchers/conjunction_matcher.rs":"687e6f1c5c8d33cec539440626790c99fb9048ca95b3e5d80f9fa531caa8674f","src/matchers/container_eq_matcher.rs":"b502076dde050afd270848c9669b0818f8f749e5afabc81441f800defdc94562","src/matchers/contains_matcher.rs":"f79b8f816a265ddb96a769e9ac48f8a6b175f127a4b1d8b24e8f4d76fca239c0","src/matchers/contains_regex_matcher.rs":"4b9e13c36ef4314ee7adbbc46e5138ebdb1fbc41f231bc9cdaad8efdb3f8cc5e","src/matchers/derefs_to_matcher.rs":"0f91f73cd0fcdb580be617f11b96b93d1055e8871554f745c76175b035e20621","src/matchers/disjunction_matcher.rs":"45f1c1fb5192f6cfd062eb05e77135b566763292589d8fe29048aaa01b357698","src/matchers/display_matcher.rs":"040296c9ad47f0dab694a7c3b3b24beecd4e02d2de011e52d432309ba97e7e02","src/matchers/each_matcher.rs":"61802a11497447a8f8144eb7be97b7600cc04fa55ed884728c4d7c14d0ca2455","src/matchers/elements_are_matcher.rs":"feccfb18fa552d94d25ca051bd9b4fe69d65004604b2ec317699f9c1bb4127ef","src/matchers/empty_matcher.rs":"7d68972d24e7d81dea6bdc77a8193d0d8c9b1988aab737ca3721b75e134b8e50","src/matchers/eq_matcher.rs":"8ea401d1e4ad5ff2f96a7178640c7d7e3b416a43e4d88086460ac4fba23e7044","src/matchers/err_matcher.rs":"0f8c9e47b76973393817d760c1b3833e4f81fd4a1cbe0bdf1237947b18c64902","src/matchers/field_matcher.rs":"01c95cca86e69266146a4331f00e2d393f88856a5aa44c2163ae9cc5e1ab348d","src/matchers/ge_matcher.rs":"cc45fae8414095959f5da830589495b0a1623666410953b027324863f8883fb0","src/matchers/gt_matcher.rs":"4c1fd646ac87f7525b3d080e78c6abf935bed50221e4dfd4339e8b75935af115","src/matchers/has_entry_matcher.rs":"3b43e0e6a24890a94910be35f0fd0c25fac602fa660d44303d769f7bd00145be","src/matchers/is_encoded_string_matcher.rs":"6519057f21db928c6dbb4d62eb7d695e992df1d660a16dbabd0c7bb73fb65113","src/matchers/is_matcher.rs":"4e842d665eb710540fbf7b274755d665727ff01f51bee57e9b2697f6aec90cd7","src/matchers/is_nan_matcher.rs":"9972b43fd35c47b89755d8ec176631f76b6cfec725d8972caed277369fc74493","src/matchers/le_matcher.rs":"108581b0fb4d9141e5aa0ec3abb640bd2828b5e39b0f3793bd767cba4eca3afb","src/matchers/len_matcher.rs":"f7312f738fc644ba942a15b017285cfc67a2458c815d5ce29e56cca307b6f357","src/matchers/lt_matcher.rs":"5966f449669160e2905acc0da5db5487d6da580bc96a6e77fb320eb8d4546990","src/matchers/matches_pattern.rs":"9895407b21823da2608be95086605f6bd508510bc987f1a840a495025b574921","src/matchers/matches_regex_matcher.rs":"5fca7eaddbe064c5053a0ebbd9532f8e00ab263101afb3948716fad4aaa0c38f","src/matchers/mod.rs":"0c0d9ca73488df9f9bed475da9d0b60ca69a5d16daf534fef77dd04d40d0290b","src/matchers/near_matcher.rs":"3ec423d88e9d52b518c9892b8271e76789971160c1f28534bce8adc250be5701","src/matchers/none_matcher.rs":"d422658c824f07afe870003383d42b090d9a7a72d539450e7903252a859352b1","src/matchers/not_matcher.rs":"140e0959a25cca75fd3aa41fad0dd71d50753b6b38220ff4a4960f2ef7d295e0","src/matchers/ok_matcher.rs":"2aae736e349bddc84b4cee5699b2e6d21817facdc49a28415a8ff919adea63bb","src/matchers/points_to_matcher.rs":"a1f55e8d9813a3651cc24307923b461f398f761b4a040d00faac623bfffeef82","src/matchers/pointwise_matcher.rs":"3b2ed83dcd216c82063697e03bad998e9c6f2d93f0c18c8edb847b1ceeea2ff4","src/matchers/predicate_matcher.rs":"825896ee6839f9b6a39f971529fc3e33d47b6f4abece86eaf911aff2af2e795e","src/matchers/property_matcher.rs":"d65d1bd8059cead41ce81db7c080ec639cf89e8d5846939b34687a1611494b24","src/matchers/result_of_matcher.rs":"fbcb266f7a65921fa799795fef21ab684706cf6b4ab79c2eb09c10c225497b8f","src/matchers/some_matcher.rs":"9ee7282180400476c39e7f43d5f028e93442423142665ecd23c397e5bd40c935","src/matchers/str_matcher.rs":"a10443574eec5c5e33c93e6208651a5f0b865a9a200517bf2e4d1f12c792a424","src/matchers/subset_of_matcher.rs":"e0966351bff9623aa63aaf7ae9403e05e6aabcdd8f4bb9074b19069cb0adff6b","src/matchers/superset_of_matcher.rs":"fd5af81f7eb390e6b6fc1c61b79963d3220d6505615171737b566e06eb441e9e","src/matchers/tuple_matcher.rs":"d00eb6fecd1d5671418e3bbe62c67f422b6a945ee1276e16edc1402c8e4f2edb","src/matchers/unordered_elements_are_matcher.rs":"fed9b464ee1597737a1ed86cd5fa0aeeb566fbd0ef3b00cc839988c3c2eb3dbc","tests/all_matcher_test.rs":"1808617e4f59b9e7ad001c45d5653d5389e761347ae4194f221767e5e6b5b7ab","tests/any_matcher_test.rs":"0ce6aa6de318e0e1f0976a04b1581f012001a9afdc1474c3a460e844e8b4f97e","tests/assertions_test.rs":"3425e5fc54a13034069305a9c30ee5fe7b9c5766a2a75d5dca721a3456b08211","tests/colorized_diff_test.rs":"01e9b2fe99aced6aad0281ba18f2442cf036e654b5fbf6c937dd957572e3ab95","tests/composition_test.rs":"bda4157c7a68001eddaabba788c72ce2db86645225cf47a105661799346eced7","tests/elements_are_matcher_test.rs":"decdfa0388dc76703ef2105fceea2bb70cc1e01c04a27208f8e68e8d6fc069af","tests/field_matcher_test.rs":"f82c36f3662e8798b7eaa7dec04b936fac2e74692cdbb90c305ae7ae41347254","tests/fmt_test.rs":"56796a45c9666bc08c7a88e62bdab9ed98b6f1d7adf0727e85c9c5bf2ebd7c31","tests/lib.rs":"e783f3e4908cb080721bdaf72ab4b6e1fb2165b5f4fb0f4baa179ad5c06dfe0f","tests/matches_pattern_test.rs":"bd5dfe2019828ae1df891f88d487bc64bb1a8ccf086fa05b28d4ddd2a785cfb5","tests/no_color_test.rs":"1bbca449ae5bdc261c96879bf5298e60b589947634bc6f8b9bdcbefdd13241a3","tests/pointwise_matcher_test.rs":"cf3e0c13c32a5e891fa47fe765e744a8d1652acc783cac9b4f0e501562df0877","tests/property_matcher_test.rs":"4461d54abaaa5135e371c516ce9b22c4b1bd187d25fea18d96562adaaf8a2839","tests/proptest_integration_test.rs":"948f4d59b7f325faa522e53f3c8a4542be349f368cb790a39c157eb6c7b47014","tests/tuple_matcher_test.rs":"cfa703ef99a2cafedb5cec8d8920fe2d146f17b0e9e1d894d473083d9d408d65","tests/unordered_elements_are_matcher_test.rs":"2fa8dbdd170567b8d02f46427b1ad5533a3f1737e0adb4fe6340b3f3dba110c7"},"package":"dce026f84cdd339bf71be01b24fe67470ee634282f68c1c4b563d00a9f002b05"}
\ No newline at end of file
diff --git a/crates/googletest/Android.bp b/crates/googletest/Android.bp
index bd6d8f8..4ebc3a8 100644
--- a/crates/googletest/Android.bp
+++ b/crates/googletest/Android.bp
@@ -18,7 +18,7 @@
     host_supported: true,
     crate_name: "googletest",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.11.0",
+    cargo_pkg_version: "0.13.0",
     crate_root: "src/lib.rs",
     edition: "2021",
     rustlibs: [
diff --git a/crates/googletest/Cargo.toml b/crates/googletest/Cargo.toml
index ed9d01c..b1d26ae 100644
--- a/crates/googletest/Cargo.toml
+++ b/crates/googletest/Cargo.toml
@@ -11,15 +11,20 @@
 
 [package]
 edition = "2021"
-rust-version = "1.66.0"
+rust-version = "1.70.0"
 name = "googletest"
-version = "0.11.0"
+version = "0.13.0"
 authors = [
     "Bradford Hovinen <[email protected]>",
     "Bastien Jacot-Guillarmod <[email protected]>",
     "Maciej Pietrzak <[email protected]>",
     "Martin Geisler <[email protected]>",
 ]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
 description = "A rich assertion and matcher library inspired by GoogleTest for C++"
 readme = "README.md"
 keywords = [
@@ -35,12 +40,80 @@
 license = "Apache-2.0"
 repository = "https://github.com/google/googletest-rust"
 
+[lib]
+name = "googletest"
+path = "src/lib.rs"
+
+[[test]]
+name = "all_matcher_test"
+path = "tests/all_matcher_test.rs"
+
+[[test]]
+name = "any_matcher_test"
+path = "tests/any_matcher_test.rs"
+
+[[test]]
+name = "assertions_test"
+path = "tests/assertions_test.rs"
+
+[[test]]
+name = "colorized_diff_test"
+path = "tests/colorized_diff_test.rs"
+
+[[test]]
+name = "composition_test"
+path = "tests/composition_test.rs"
+
+[[test]]
+name = "elements_are_matcher_test"
+path = "tests/elements_are_matcher_test.rs"
+
+[[test]]
+name = "field_matcher_test"
+path = "tests/field_matcher_test.rs"
+
+[[test]]
+name = "fmt_test"
+path = "tests/fmt_test.rs"
+
+[[test]]
+name = "lib"
+path = "tests/lib.rs"
+
+[[test]]
+name = "matches_pattern_test"
+path = "tests/matches_pattern_test.rs"
+
+[[test]]
+name = "no_color_test"
+path = "tests/no_color_test.rs"
+
+[[test]]
+name = "pointwise_matcher_test"
+path = "tests/pointwise_matcher_test.rs"
+
+[[test]]
+name = "property_matcher_test"
+path = "tests/property_matcher_test.rs"
+
+[[test]]
+name = "proptest_integration_test"
+path = "tests/proptest_integration_test.rs"
+
+[[test]]
+name = "tuple_matcher_test"
+path = "tests/tuple_matcher_test.rs"
+
+[[test]]
+name = "unordered_elements_are_matcher_test"
+path = "tests/unordered_elements_are_matcher_test.rs"
+
 [dependencies.anyhow]
 version = "1"
 optional = true
 
 [dependencies.googletest_macro]
-version = "0.11.0"
+version = "0.13.0"
 
 [dependencies.num-traits]
 version = "0.2.17"
@@ -60,6 +133,3 @@
 
 [dev-dependencies.quickcheck]
 version = "1.0.3"
-
-[dev-dependencies.serial_test]
-version = "2.0.0"
diff --git a/crates/googletest/METADATA b/crates/googletest/METADATA
index 8b0b3e2..4b1ad07 100644
--- a/crates/googletest/METADATA
+++ b/crates/googletest/METADATA
@@ -1,17 +1,17 @@
 name: "googletest"
 description: "A rich assertion and matcher library inspired by GoogleTest for C++"
 third_party {
-  version: "0.11.0"
+  version: "0.13.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2024
-    month: 2
-    day: 2
+    year: 2025
+    month: 1
+    day: 3
   }
   homepage: "https://crates.io/crates/googletest"
   identifier {
     type: "Archive"
-    value: "https://static.crates.io/crates/googletest/googletest-0.11.0.crate"
-    version: "0.11.0"
+    value: "https://static.crates.io/crates/googletest/googletest-0.13.0.crate"
+    version: "0.13.0"
   }
 }
diff --git a/crates/googletest/README.md b/crates/googletest/README.md
index d442770..c611e79 100644
--- a/crates/googletest/README.md
+++ b/crates/googletest/README.md
@@ -25,7 +25,7 @@
  * A new set of assertion macros offering similar functionality to those of
    [GoogleTest](https://google.github.io/googletest/primer.html#assertions).
 
-**The minimum supported Rust version is 1.66**.
+**The minimum supported Rust version is 1.70**.
 
 > :warning: The API is not fully stable and may still be changed until we
 > publish version 1.0.
@@ -34,6 +34,15 @@
 > not be used directly. Those items or modules are only for internal uses and
 > their API may change without a major version update.
 
+## Learning resources
+
+If you're just getting started with `googletest`, consider going through
+the first chapter of
+["Advanced testing for Rust applications"](https://github.com/mainmatter/rust-advanced-testing-workshop),
+a self-guided Rust course: it provides a guided introduction to the library,
+with exercises to help you get comfortable with `googletest` macros,
+its matchers and its overall philosophy.
+
 ## Assertions and matchers
 
 The core of GoogleTest is its *matchers*. Matchers indicate what aspect of an
@@ -45,7 +54,7 @@
  * [`assert_that!`] panics if the assertion fails, aborting the test.
  * [`expect_that!`] logs an assertion failure, marking the test as having
    failed, but allows the test to continue running (called a _non-fatal
-   assertion_). It requires the use of the [`googletest::test`] attribute macro
+   assertion_). It requires the use of the [`gtest`] attribute macro
    on the test itself.
  * [`verify_that!`] has no side effects and evaluates to a [`Result<()>`] whose
    `Err` variant describes the assertion failure, if there is one. In
@@ -65,7 +74,7 @@
     assert_that!(value, eq(4));
 }
 
-#[googletest::test]
+#[gtest]
 fn two_logged_failures() {
     let value = 2;
     expect_that!(value, eq(4)); // Test now failed, but continues executing.
@@ -98,7 +107,7 @@
 ```rust
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn contains_at_least_one_item_at_least_3() {
     let value = vec![1, 2, 3];
     expect_that!(value, contains(ge(3)));
@@ -110,7 +119,7 @@
 ```rust
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn strictly_between_9_and_11() {
     let value = 10;
     expect_that!(value, gt(9).and(not(ge(11))));
@@ -156,11 +165,9 @@
     expected: T,
 }
 
-impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
-    type ActualT = T;
-
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-         (self.expected == *actual).into()
+impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+    fn matches(&self, actual: T) -> MatcherResult {
+         (self.expected == actual).into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> String {
@@ -179,7 +186,7 @@
 It is recommended to expose a function which constructs the matcher:
 
 ```rust
-pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
+pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<T> {
     MyEqMatcher { expected }
 }
 ```
@@ -187,7 +194,7 @@
 The new matcher can then be used in the assertion macros:
 
 ```rust
-#[googletest::test]
+#[gtest]
 fn should_be_equal_by_my_definition() {
     expect_that!(10, eq_my_way(10));
 }
@@ -202,12 +209,12 @@
 This is analogous to the `EXPECT_*` family of macros in GoogleTest.
 
 To make a non-fatal assertion, use the macro [`expect_that!`]. The test must
-also be marked with [`googletest::test`] instead of the Rust-standard `#[test]`.
+also be marked with [`gtest`] instead of the Rust-standard `#[test]`.
 
 ```rust
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn three_non_fatal_assertions() {
     let value = 2;
     expect_that!(value, eq(2));  // Passes; test still considered passing.
@@ -222,7 +229,7 @@
 ```rust
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn failing_non_fatal_assertion() -> Result<()> {
     let value = 2;
     expect_that!(value, eq(3));  // Just marks the test as having failed.
@@ -235,7 +242,7 @@
 ```rust
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn failing_fatal_assertion_after_non_fatal_assertion() -> Result<()> {
     let value = 2;
     verify_that!(value, eq(3))?; // Fails and aborts the test.
@@ -246,12 +253,12 @@
 
 ### Interoperability
 
-You can use the `#[googletest::test]` macro together with many other libraries
+You can use the `#[gtest]` macro together with many other libraries
 such as [rstest](https://crates.io/crates/rstest). Just apply both attribute
 macros to the test:
 
 ```rust
-#[googletest::test]
+#[gtest]
 #[rstest]
 #[case(1)]
 #[case(2)]
@@ -261,16 +268,16 @@
 }
 ```
 
-Make sure to put `#[googletest::test]` *before* `#[rstest]`. Otherwise the
+Make sure to put `#[gtest]` *before* `#[rstest]`. Otherwise the
 annotated test will run twice, since both macros will attempt to register a test
 with the Rust test harness.
 
 The macro also works together with
-[async tests with Tokio](https://docs.rs/tokio/latest/tokio/attr.test.html) in
+[async tests with Tokio](https://docs.rs/tokio/latest/tokio/attr.gtest.html) in
 the same way:
 
 ```rust
-#[googletest::test]
+#[gtest]
 #[tokio::test]
 async fn should_work_with_tokio() -> Result<()> {
     verify_that!(3, gt(0))
@@ -348,7 +355,7 @@
 [`expect_pred!`]: https://docs.rs/googletest/*/googletest/macro.expect_pred.html
 [`expect_that!`]: https://docs.rs/googletest/*/googletest/macro.expect_that.html
 [`fail!`]: https://docs.rs/googletest/*/googletest/macro.fail.html
-[`googletest::test`]: https://docs.rs/googletest/*/googletest/attr.test.html
+[`gtest`]: https://docs.rs/googletest/*/googletest/attr.gtest.html
 [`matches_pattern!`]: https://docs.rs/googletest/*/googletest/macro.matches_pattern.html
 [`verify_pred!`]: https://docs.rs/googletest/*/googletest/macro.verify_pred.html
 [`verify_that!`]: https://docs.rs/googletest/*/googletest/macro.verify_that.html
diff --git a/crates/googletest/crate_docs.md b/crates/googletest/crate_docs.md
index 8c8b47c..3a6b037 100644
--- a/crates/googletest/crate_docs.md
+++ b/crates/googletest/crate_docs.md
@@ -6,6 +6,14 @@
    range of assertions on data,
  * A rich set of matchers, and
  * A new set of test assertion macros.
+## Learning resources
+
+If you're just getting started with `googletest`, consider going through
+the first chapter of
+["Advanced testing for Rust applications"](https://github.com/mainmatter/rust-advanced-testing-workshop),
+a self-guided Rust course: it provides a guided introduction to the library,
+with exercises to help you get comfortable with `googletest` macros,
+its matchers and its overall philosophy.
 
 ## Assertions and matchers
 
@@ -18,7 +26,7 @@
  * [`assert_that!`] panics if the assertion fails, aborting the test.
  * [`expect_that!`] logs an assertion failure, marking the test as having
    failed, but allows the test to continue running (called a _non-fatal
-   assertion_). It requires the use of the [`googletest::test`][crate::test]
+   assertion_). It requires the use of the [`gtest`]
    attribute macro on the test itself.
  * [`verify_that!`] has no side effects and evaluates to a [`Result`] whose
    `Err` variant describes the assertion failure, if there is one. In
@@ -41,7 +49,7 @@
 }
 
 # /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
 # */
 fn two_logged_failures() {
     let value = 2;
@@ -74,25 +82,25 @@
 use googletest::prelude::*;
 
 # /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
 # */
 fn contains_at_least_one_item_at_least_3() {
 # googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
     let value = vec![1, 2, 3];
-    expect_that!(value, contains(ge(3)));
+    expect_that!(value, contains(ge(&3)));
 # googletest::internal::test_outcome::TestOutcome::close_current_test_outcome::<&str>(Ok(()))
 #     .unwrap();
 }
 # contains_at_least_one_item_at_least_3();
 ```
 
-They can also be logically combined:
+They can also be logically combined, with methods from [`MatcherBase`]:
 
 ```
 use googletest::prelude::*;
 
 # /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
 # */
 fn strictly_between_9_and_11() {
 # googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
@@ -120,13 +128,13 @@
 | [`contains_each!`]   | A container containing distinct elements each of the arguments match.    |
 | [`contains_regex`]   | A string containing a substring matching the given regular expression.   |
 | [`contains_substring`] | A string containing the given substring.                               |
+| [`derefs_to`]        | A [`Deref`] which `deref()`s to a value that the argument matches.       |
 | [`displays_as`]      | A [`Display`] value whose formatted string is matched by the argument.   |
 | [`each`]             | A container all of whose elements the given argument matches.            |
 | [`elements_are!`]    | A container whose elements the arguments match, in order.                |
 | [`empty`]            | An empty collection.                                                     |
 | [`ends_with`]        | A string ending with the given suffix.                                   |
 | [`eq`]               | A value equal to the argument, in the sense of the [`PartialEq`] trait.  |
-| [`eq_deref_of`]      | A value equal to the dereferenced value of the argument.                 |
 | [`err`]              | A [`Result`][std::result::Result] containing an `Err` variant the argument matches. |
 | [`field!`]           | A struct or enum with a given field whose value the argument matches.    |
 | [`ge`]               | A [`PartialOrd`] value greater than or equal to the given value.         |
@@ -144,7 +152,7 @@
 | [`not`]              | Any value the argument does not match.                                   |
 | [`ok`]               | A [`Result`][std::result::Result] containing an `Ok` variant the argument matches. |
 | [`pat!`]             | Alias for [`matches_pattern!`].                                          |
-| [`points_to`]        | Any [`Deref`] such as `&`, `Rc`, etc. whose value the argument matches.  |
+| [`points_to`]        | A reference `&` which points to a value that the argument matches.       |
 | [`pointwise!`]       | A container whose contents the arguments match in a pointwise fashion.   |
 | [`predicate`]        | A value on which the given predicate returns true.                       |
 | [`some`]             | An [`Option`] containing `Some` whose value the argument matches.        |
@@ -164,12 +172,12 @@
 [`contains_regex`]: matchers::contains_regex
 [`contains_substring`]: matchers::contains_substring
 [`displays_as`]: matchers::displays_as
+[`derefs_to`]: matchers::derefs_to
 [`each`]: matchers::each
 [`elements_are!`]: matchers::elements_are
 [`empty`]: matchers::empty
 [`ends_with`]: matchers::ends_with
 [`eq`]: matchers::eq
-[`eq_deref_of`]: matchers::eq_deref_of
 [`err`]: matchers::err
 [`field!`]: matchers::field
 [`ge`]: matchers::ge
@@ -205,22 +213,21 @@
 ## Writing matchers
 
 One can extend the library by writing additional matchers. To do so, create
-a struct holding the matcher's data and have it implement the trait
-[`Matcher`]:
+a struct holding the matcher's data and have it implement the traits
+[`Matcher`] and  [`MatcherBase`]:
 
 ```no_run
-use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
+use googletest::{description::Description, matcher::{Matcher, MatcherBase, MatcherResult}};
 use std::fmt::Debug;
 
+#[derive(MatcherBase)]
 struct MyEqMatcher<T> {
     expected: T,
 }
 
-impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
-    type ActualT = T;
-
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-        if self.expected == *actual {
+impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+    fn matches(&self, actual: T) -> MatcherResult {
+        if self.expected == actual {
             MatcherResult::Match
         } else {
             MatcherResult::NoMatch
@@ -243,18 +250,16 @@
  It is recommended to expose a function which constructs the matcher:
 
  ```no_run
- # use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
+ # use googletest::{description::Description, matcher::{Matcher, MatcherBase, MatcherResult}};
  # use std::fmt::Debug;
- #
+ # #[derive(MatcherBase)]
  # struct MyEqMatcher<T> {
  #    expected: T,
  # }
  #
- # impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
- #    type ActualT = T;
- #
- #    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- #        if self.expected == *actual {
+ # impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+ #    fn matches(&self, actual: T) -> MatcherResult {
+ #        if self.expected == actual {
  #            MatcherResult::Match
  #        } else {
  #            MatcherResult::NoMatch
@@ -273,7 +278,7 @@
  #    }
  # }
  #
- pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
+ pub fn eq_my_way<T: PartialEq + Debug + Copy>(expected: T) -> impl Matcher<T> {
     MyEqMatcher { expected }
  }
  ```
@@ -282,18 +287,16 @@
 
 ```
 # use googletest::prelude::*;
-# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
+# use googletest::{description::Description, matcher::{Matcher, MatcherBase, MatcherResult}};
 # use std::fmt::Debug;
-#
+# #[derive(MatcherBase)]
 # struct MyEqMatcher<T> {
 #    expected: T,
 # }
 #
-# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
-#    type ActualT = T;
-#
-#    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-#        if self.expected == *actual {
+# impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+#    fn matches(&self, actual: T) -> MatcherResult {
+#        if self.expected == actual {
 #            MatcherResult::Match
 #        } else {
 #            MatcherResult::NoMatch
@@ -312,11 +315,11 @@
 #    }
 # }
 #
-# pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
+# pub fn eq_my_way<T: PartialEq + Debug + Copy>(expected: T) -> impl Matcher<T> {
 #    MyEqMatcher { expected }
 # }
 # /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
 # */
 fn should_be_equal_by_my_definition() {
 # googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
@@ -335,13 +338,12 @@
 aborts.
 
 To make a non-fatal assertion, use the macro [`expect_that!`]. The test must
-also be marked with [`googletest::test`][crate::test] instead of the
-Rust-standard `#[test]`.
+also be marked with [`gtest`] instead of the Rust-standard `#[test]`.
 
 ```no_run
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn three_non_fatal_assertions() {
     let value = 2;
     expect_that!(value, eq(2));  // Passes; test still considered passing.
@@ -357,7 +359,7 @@
 use googletest::prelude::*;
 
 # /* Make sure this also compiles as a doctest.
-#[googletest::test]
+#[gtest]
 # */
 fn failing_non_fatal_assertion() -> Result<()> {
     let value = 2;
@@ -371,7 +373,7 @@
 ```no_run
 use googletest::prelude::*;
 
-#[googletest::test]
+#[gtest]
 fn failing_fatal_assertion_after_non_fatal_assertion() -> Result<()> {
     let value = 2;
     expect_that!(value, eq(2));  // Passes; test still considered passing.
@@ -434,62 +436,98 @@
 # always_fails().unwrap_err();
 ```
 
+## Conversion from `Result::Err` and `Option::None`
+
+To simplify error management during a test arrangement, [`Result<T>`]
+provides a few conversion utilities.
+
+If your setup function returns `std::result::Result<T, E>` where `E: std::error::Error`,
+the `std::result::Result<T, E>` can simply be handled with the `?` operator. If an `Err(e)`
+is returned, the test will report a failure at the line where the `?` operator has been
+applied (or the lowest caller without `#[track_caller]`).
+
+```
+# use googletest::prelude::*;
+struct PngImage { h: i32, w: i32 /* ... */ }
+impl PngImage {
+  fn new_from_file(file_name: &str) -> std::result::Result<Self, std::io::Error> {
+    Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))
+
+   }
+  fn rotate(&mut self) { std::mem::swap(&mut self.h, &mut self.w);}
+  fn dimensions(&self) -> (i32, i32) { (self.h, self.w)}
+}
+
+fn test_png_image_dimensions() -> googletest::Result<()> {
+  // Arrange
+  let mut png = PngImage::new_from_file("example.png")?;
+  verify_eq!(png.dimensions(), (128, 64))?;
+
+  // Act
+  png.rotate();
+
+  // Assert
+  expect_eq!(png.dimensions(), (64, 128));
+  Ok(())
+}
+
+# test_png_image_dimensions().unwrap_err();
+```
+
+If your setup function returns `Option<T>` or `std::result::Result<T, E>` where
+`E: !std::error::Error`, then you can convert these types with `into_test_result()`
+from the `IntoTestResult` extension trait.
+
+```
+# use googletest::prelude::*;
+# struct PngImage;
+# static PNG_BINARY: [u8;0] = [];
+
+impl PngImage {
+  fn new_from_binary(bin: &[u8]) -> std::result::Result<Self, String> {
+    Err("Parsing failed".into())
+  }
+}
+
+# /* The attribute macro would prevent the function from being compiled in a doctest.
+#[gtest]
+# */
+fn test_png_image_binary() -> googletest::Result<()> {
+  // Arrange
+  let png_image = PngImage::new_from_binary(&PNG_BINARY).into_test_result()?;
+  /* ... */
+  # Ok(())
+}
+# test_png_image_binary().unwrap_err();
+
+impl PngImage {
+  fn new_from_cache(key: u64) -> Option<Self> {
+    None
+  }
+}
+
+# /* The attribute macro would prevent the function from being compiled in a doctest.
+#[gtest]
+# */
+fn test_png_from_cache() -> googletest::Result<()> {
+  // Arrange
+  let png_image = PngImage::new_from_cache(123).into_test_result()?;
+  /* ... */
+  # Ok(())
+}
+# test_png_from_cache().unwrap_err();
+```
+
+
 ## Integrations with other crates
 
 GoogleTest Rust includes integrations with the
-[Anyhow](https://crates.io/crates/anyhow) and
 [Proptest](https://crates.io/crates/proptest) crates to simplify turning
-errors from those crates into test failures.
-
-To use this, activate the `anyhow`, respectively `proptest` feature in
-GoogleTest Rust and invoke the extension method [`into_test_result()`] on a
-`Result` value in your test. For example:
-
-```
-# use googletest::prelude::*;
-# #[cfg(feature = "anyhow")]
-# use anyhow::anyhow;
-# #[cfg(feature = "anyhow")]
-# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[test]
-# */
-fn has_anyhow_failure() -> Result<()> {
-    Ok(just_return_error().into_test_result()?)
-}
-
-# #[cfg(feature = "anyhow")]
-fn just_return_error() -> anyhow::Result<()> {
-    anyhow::Result::Err(anyhow!("This is an error"))
-}
-# #[cfg(feature = "anyhow")]
-# has_anyhow_failure().unwrap_err();
-```
-
-One can convert Proptest test failures into GoogleTest test failures when the
-test is invoked with
-[`TestRunner::run`](https://docs.rs/proptest/latest/proptest/test_runner/struct.TestRunner.html#method.run):
-
-```
-# use googletest::prelude::*;
-# #[cfg(feature = "proptest")]
-# use proptest::test_runner::{Config, TestRunner};
-# #[cfg(feature = "proptest")]
-# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[test]
-# */
-fn numbers_are_greater_than_zero() -> Result<()> {
-    let mut runner = TestRunner::new(Config::default());
-    runner.run(&(1..100i32), |v| Ok(verify_that!(v, gt(0))?)).into_test_result()
-}
-# #[cfg(feature = "proptest")]
-# numbers_are_greater_than_zero().unwrap();
-```
-
-Similarly, when the `proptest` feature is enabled, GoogleTest assertion failures
-can automatically be converted into Proptest
+GoogleTest assertion failures into Proptest
 [`TestCaseError`](https://docs.rs/proptest/latest/proptest/test_runner/enum.TestCaseError.html)
-through the `?` operator as the example above shows.
+through the `?` operator.
 
 [`and_log_failure()`]: GoogleTestSupport::and_log_failure
 [`into_test_result()`]: IntoTestResult::into_test_result
 [`Matcher`]: matcher::Matcher
+[`MatcherBase`]: matcher::MatcherBase
diff --git a/crates/googletest/src/assertions.rs b/crates/googletest/src/assertions.rs
index 2668028..ac5177e 100644
--- a/crates/googletest/src/assertions.rs
+++ b/crates/googletest/src/assertions.rs
@@ -120,29 +120,37 @@
 /// not supported; see [Rust by Example](https://doc.rust-lang.org/rust-by-example/primitives/tuples.html#tuples).
 #[macro_export]
 macro_rules! verify_that {
+    // specialized to sequences:
     ($actual:expr, [$($expecteds:expr),+ $(,)?]) => {
-        $crate::assertions::internal::check_matcher(
-            &$actual,
-            $crate::matchers::elements_are![$($expecteds),+],
-            stringify!($actual),
-            $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
-        )
+        {
+            use $crate::assertions::internal::Subject as _;
+            $actual.check(
+                $crate::matchers::elements_are![$($expecteds),+],
+                stringify!($actual),
+            )
+        }
     };
+
+    // specialized to unordered sequences:
     ($actual:expr, {$($expecteds:expr),+ $(,)?}) => {
-        $crate::assertions::internal::check_matcher(
-            &$actual,
-            $crate::matchers::unordered_elements_are![$($expecteds),+],
-            stringify!($actual),
-            $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
-        )
+        {
+            use $crate::assertions::internal::Subject as  _;
+            $actual.check(
+                $crate::matchers::unordered_elements_are![$($expecteds),+],
+                stringify!($actual),
+            )
+        }
     };
+
+    // general case:
     ($actual:expr, $expected:expr $(,)?) => {
-        $crate::assertions::internal::check_matcher(
-            &$actual,
-            $expected,
-            stringify!($actual),
-            $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
-        )
+        {
+            use $crate::assertions::internal::Subject as  _;
+            $actual.check(
+                $expected,
+                stringify!($actual),
+            )
+        }
     };
 }
 
@@ -165,30 +173,25 @@
 /// # */
 /// fn test() -> Result<()> {
 ///     let a = 1;
-///     let b = 7;
-///     let n = 5;
-///     verify_pred!(equals_modulo(a, b, n))?;
+///     fn b(_x: i32) -> i32 { 7 }
+///     verify_pred!(equals_modulo(a, b(b(2)), 2 + 3))?;
 ///     Ok(())
 /// }
 /// # verify_that!(
 /// #     test(),
-/// #     err(displays_as(contains_substring("equals_modulo(a, b, n) was false with")))
+/// #     err(displays_as(contains_substring("equals_modulo(a, b(b(2)), 2 + 3) was false with")))
 /// # ).unwrap();
 /// ```
 ///
 /// This results in the following message:
 ///
 /// ```text
-/// equals_modulo(a, b, n) was false with
+/// equals_modulo(a, b(b(2)), 2 + 3) was false with
 ///   a = 1,
-///   b = 7,
-///   n = 5
+///   b(b(2)) = 7,
+///   2 + 3 = 5,
 /// ```
 ///
-/// The function passed to this macro must return `bool`. Each of the arguments
-/// must evaluate to a type implementing [`std::fmt::Debug`]. The debug output
-/// is used to construct the failure message.
-///
 /// The predicate can also be a method on a struct, e.g.:
 ///
 /// ```ignore
@@ -201,42 +204,45 @@
 /// verify_pred!((AStruct {}).equals_modulo(a, b, n))?;
 /// ```
 ///
-/// **Warning:** This macro assumes that the arguments passed to the predicate
-/// are either *variables* or *calls to pure functions*. If two subsequent
-/// invocations to any of the expresssions passed as arguments result in
-/// different values, then the output message of a test failure will deviate
-/// from the values actually passed to the predicate. For this reason, *always
-/// assign the outputs of non-pure functions to variables before using them in
-/// this macro. For example:
+/// The expression passed to this macro must return `bool`. In the most general
+/// case, it prints out each of the `.`-separated parts of the expression and
+/// the arguments of all top-level method calls as long as they implement
+/// `Debug`. It evaluates every value (including the method receivers) exactly
+/// once. Effectively, for `verify_pred!((a + 1).b.c(x + y, &mut z, 2))`, it
+/// generates code analogous to the following, which allows printing accurate
+/// intermediate values even if they are subsequently consumed (moved out) or
+/// mutated in-place by the expression:
 ///
 /// ```ignore
-/// let output = generate_random_number();  // Assigned outside of verify_pred.
-/// verify_pred!(is_sufficiently_random(output))?;
+/// let mut error_message = "(a + 1).b.c(x + y, 2) was false with".to_string();
+/// let mut x1 = (a + 1);
+/// write!(error_message, "\n  (a + 1) = {:?},", x1);
+/// write!(error_message, "\n  (a + 1).b = {:?},", x1.b);
+/// let mut x2 = x + y;
+/// write!(error_message, "\n  x + y = {:?},", x2);
+/// let mut x3 = &mut z;
+/// write!(error_message, "\n  & mut z = {:?},", x3);
+/// let mut x4 = x1.b.c(x2, x3, 2);
+/// if (x4) {
+///   Ok(())
+/// } else {
+///   Err(error_message)
+/// }
 /// ```
+///
+/// Wrapping the passed-in expression in parens or curly braces will prevent the
+/// detailed printing of the expression.
+///
+/// ```ignore
+/// verify_pred!((a.foo()).bar())?;
+/// ```
+///
+/// would not print `a`, but would print `(a.foo())` and `(a.foo()).bar()` on
+/// error.
 #[macro_export]
 macro_rules! verify_pred {
-    ([$($predicate:tt)*]($($arg:tt),* $(,)?)) => {
-        if !$($predicate)*($($arg),*) {
-            $crate::assertions::internal::report_failed_predicate(
-                concat!(stringify!($($predicate)*), stringify!(($($arg),*))),
-                vec![$((format!(concat!(stringify!($arg), " = {:?}"), $arg))),*],
-                $crate::internal::source_location::SourceLocation::new(
-                    file!(),
-                    line!(),
-                    column!(),
-                ),
-            )
-        } else {
-            Ok(())
-        }
-    };
-
-    ([$($predicate:tt)*] $first:tt $($rest:tt)*) => {
-        $crate::verify_pred!([$($predicate)* $first] $($rest)*)
-    };
-
-    ($first:tt $($rest:tt)*) => {
-        $crate::verify_pred!([$first] $($rest)*)
+    ($expr:expr $(,)?) => {
+        $crate::assertions::internal::__googletest_macro_verify_pred!($expr)
     };
 }
 
@@ -285,27 +291,978 @@
 /// [`and_log_failure`](crate::GoogleTestSupport::and_log_failure).
 #[macro_export]
 macro_rules! fail {
-    ($($message:expr),+) => {{
-        // We wrap this in a function so that we can annotate it with the must_use attribute.
-        // must_use on expressions is still experimental.
-        #[must_use = "The assertion result must be evaluated to affect the test result."]
-        fn create_fail_result(message: String) -> $crate::Result<()> {
-            Err($crate::internal::test_outcome::TestAssertionFailure::create(format!(
-                "{}\n{}",
-                message,
-                $crate::internal::source_location::SourceLocation::new(
-                    file!(),
-                    line!(),
-                    column!(),
-                ),
-            )))
-        }
-        create_fail_result(format!($($message),*))
+    ($($message:expr),+ $(,)?) => {{
+        $crate::assertions::internal::create_fail_result(
+            format!($($message),*),
+        )
     }};
 
     () => { fail!("Test failed") };
 }
 
+/// Generates a success. This **does not** make the overall test succeed. A test
+/// is only considered successful if none of its assertions fail during its
+/// execution.
+///
+/// The succeed!() assertion is purely documentary. The only user visible output
+/// is a stdout with information on where the success was generated from.
+///
+/// ```ignore
+/// fn test_to_be_implemented() {
+///     succeed!();
+/// }
+/// ```
+///
+/// One may include formatted arguments in the success message:
+///
+/// ```ignore
+/// fn test_to_be_implemented() {
+///     succeed!("I am just a fake test: {}", "a fake test indeed");
+/// }
+/// ```
+#[macro_export]
+macro_rules! succeed {
+    ($($message:expr),+ $(,)?) => {{
+        println!(
+            "{}\n at {}:{}:{}",
+            format!($($message),*),
+            file!(), line!(), column!()
+        );
+    }};
+
+    () => {
+        succeed!("Success")
+    };
+}
+
+/// Generates a failure marking the test as failed but continue execution.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+///     add_failure!();
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+///     add_failure!("I am just a fake test: {}", "a fake test indeed");
+/// }
+/// ```
+#[macro_export]
+macro_rules! add_failure {
+    ($($message:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        $crate::assertions::internal::create_fail_result(
+            format!($($message),*),
+        ).and_log_failure();
+    }};
+
+    () => {
+        add_failure!("Failed")
+    };
+}
+
+/// Generates a failure at specified location marking the test as failed but
+/// continue execution.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+///     add_failure_at!("src/my_file.rs", 32, 12);
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+///     add_failure_at!(
+///         "src/my_file.rs",
+///         32,
+///         12,
+///         "I am just a fake test: {}", "a fake test indeed",
+///     );
+/// }
+/// ```
+#[macro_export]
+macro_rules! add_failure_at {
+    ($file:expr, $line:expr, $column:expr, $($message:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        $crate::assertions::internal::create_fail_result(
+            format!($($message),*),
+        ).map_err(|e| e.with_fake_location($file, $line, $column)).and_log_failure();
+    }};
+
+    ($file:expr, $line:expr, $column:expr $(,)?) => {
+        add_failure_at!($file, $line, $column, "Failed")
+    };
+}
+
+/// Verify if the condition evaluates to true and returns `Result`.
+///
+/// Evaluates to `Result::Ok(())` if the condition is true and
+/// `Result::Err(TestAssertionFailure)` if it evaluates to false. The caller
+/// must then decide how to handle the `Err` variant. It has a few options:
+///   * Abort the current function with the `?` operator. This requires that the
+///     function return a suitable `Result`.
+///   * Log the failure and continue by calling the method `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_true!(2 + 2 == 5)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_true {
+    ($condition:expr) => {{
+        use $crate::assertions::internal::Subject as _;
+        ($condition).check($crate::matchers::eq(true), stringify!($condition))
+    }};
+}
+
+/// Marks test as failed and continue execution if the expression evaluates to
+/// false.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_true!(2 + 2 == 5);
+///     println!("This will print");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_true {
+    ($condition:expr) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_true!($condition).and_log_failure()
+    }};
+}
+
+/// Verify if the condition evaluates to false and returns `Result`.
+///
+/// Evaluates to `Result::Ok(())` if the condition is false and
+/// `Result::Err(TestAssertionFailure)` if it evaluates to true. The caller
+/// must then decide how to handle the `Err` variant. It has a few options:
+///   * Abort the current function with the `?` operator. This requires that the
+///     function return a suitable `Result`.
+///   * Log the failure and continue by calling the method `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_false!(2 + 2 == 4)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_false {
+    ($condition:expr) => {{
+        use $crate::assertions::internal::Subject as _;
+        ($condition).check($crate::matchers::eq(false), stringify!($condition))
+    }};
+}
+
+/// Marks test as failed and continue execution if the expression evaluates to
+/// true.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_false!(2 + 2 == 4);
+///     println!("This will print");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_false {
+    ($condition:expr) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_false!($condition).and_log_failure()
+    }};
+}
+
+/// Checks whether the second argument is equal to the first argument.
+///
+/// Evaluates to `Result::Ok(())` if they are equal and
+/// `Result::Err(TestAssertionFailure)` if they are not. The caller must then
+/// decide how to handle the `Err` variant. It has a few options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_eq!(2, 1)
+/// }
+/// ```
+///
+/// This macro has special support for matching against container. Namely:
+///  * `verify_eq!(actual, [e1, e2, ...])` is equivalent to
+///    `verify_that!(actual, elements_are![eq(e1), eq(e2), ...])`
+///  * `verify_eq!(actual, {e1, e2, ...})` is equivalent to
+///    `verify_that!(actual, unordered_elements_are![eq(e1), eq(e2), ...])`
+#[macro_export]
+macro_rules! verify_eq {
+    // Specialization for ordered sequences of tuples:
+    ($actual:expr, [ $( ( $($tuple_elt:expr),* ) ),+ $(,)? ] $(,)?) => {
+        verify_that!(&$actual, [
+            $(
+                // tuple matching
+                (
+                    $(
+                        $crate::matchers::eq(&$tuple_elt)
+                    ),*
+                )
+            ),*
+        ])
+    };
+
+    // Specialization for unordered sequences of tuples:
+    ($actual:expr, { $( ( $($tuple_elt:expr),* ) ),+ $(,)?} $(,)?) => {
+        verify_that!(&$actual, {
+            $(
+                // tuple matching
+                (
+                    $(
+                        $crate::matchers::eq(&$tuple_elt)
+                    ),*
+                )
+            ),*
+        })
+    };
+
+    // Ordered sequences:
+    ($actual:expr, [$($expected:expr),+ $(,)?] $(,)?) => {
+        verify_that!(&$actual, [$($crate::matchers::eq(&$expected)),*])
+    };
+
+    // Unordered sequences:
+    ($actual:expr, {$($expected:expr),+ $(,)?} $(,)?) => {
+        verify_that!(&$actual, {$($crate::matchers::eq(&$expected)),*})
+    };
+
+    // General case:
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!(&$actual, $crate::matchers::eq(&$expected))
+    };
+}
+
+/// Marks test as failed and continues execution if the second argument is not
+/// equal to first argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_eq!(2, 1);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// This macro has special support for matching against container. Namely:
+///  * `expect_eq!(actual, [e1, e2, ...])` for checking actual contains "e1, e2,
+///    ..." in order.
+///  * `expect_eq!(actual, {e1, e2, ...})` for checking actual contains "e1, e2,
+///    ..." in any order.
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_eq!(2, 1, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_eq {
+    ($actual:expr, [$($expected:expr),+ $(,)?] $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_eq!($actual, [$($expected),*]).and_log_failure();
+    }};
+    ($actual:expr, [$($expected:expr),+ $(,)?], $($format_args:expr),* $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_eq!($actual, [$($expected),*])
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, {$($expected:expr),+ $(,)?} $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_eq!($actual, {$($expected),*}).and_log_failure();
+    }};
+    ($actual:expr, {$($expected:expr),+ $(,)?}, $($format_args:expr),* $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_eq!($actual, {$($expected),*})
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_eq!($actual, $expected).and_log_failure();
+    }};
+    ($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_eq!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+}
+
+/// Checks whether the second argument is not equal to the first argument.
+///
+/// Evaluates to `Result::Ok(())` if they are not equal and
+/// `Result::Err(TestAssertionFailure)` if they are equal. The caller must then
+/// decide how to handle the `Err` variant. It has a few options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_ne!(1, 1)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_ne {
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!(&$actual, $crate::matchers::not($crate::matchers::eq(&$expected)))
+    };
+}
+
+/// Marks test as failed and continues execution if the second argument is
+/// equal to first argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_ne!(1, 1);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_ne!(1, 1, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_ne {
+    ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_ne!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_ne!($actual, $expected).and_log_failure();
+    }};
+}
+
+/// Checks whether the first argument is less than second argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is less than the second
+/// and `Result::Err(TestAssertionFailure)` if it is greater or equal. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_lt!(2, 1)
+/// }
+#[macro_export]
+macro_rules! verify_lt {
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!($actual, $crate::matchers::lt($expected))
+    };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// greater or equal to second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_lt!(2, 1);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_lt!(1, 1, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_lt {
+    ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_lt!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_lt!($actual, $expected).and_log_failure();
+    }};
+}
+
+/// Checks whether the first argument is less than or equal to the second
+/// argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is less than or equal to
+/// the second and `Result::Err(TestAssertionFailure)` if it is greater. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_le!(2, 1)
+/// }
+#[macro_export]
+macro_rules! verify_le {
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!($actual, $crate::matchers::le($expected))
+    };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// greater than the second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_le!(2, 1);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_le!(2, 1, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_le {
+    ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_le!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_le!($actual, $expected).and_log_failure();
+    }};
+}
+
+/// Checks whether the first argument is greater than the second argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is greater than
+/// the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_gt!(1, 2)
+/// }
+#[macro_export]
+macro_rules! verify_gt {
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!($actual, $crate::matchers::gt($expected))
+    };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// not greater than the second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_gt!(1, 2);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_gt!(1, 2, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_gt {
+    ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_gt!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_gt!($actual, $expected).and_log_failure();
+    }};
+}
+
+/// Checks whether the first argument is greater than or equal to the second
+/// argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is greater than or equal
+/// to the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_ge!(1, 2)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_ge {
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!($actual, $crate::matchers::ge($expected))
+    };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// not greater than or equal to the second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_ge!(1, 2);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_ge!(1, 2, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_ge {
+    ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_ge!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_ge!($actual, $expected).and_log_failure();
+    }};
+}
+
+/// Checks whether the float given by first argument is approximately
+/// equal to second argument.
+///
+/// This automatically computes a tolerance from the magnitude of `expected` and
+/// matches any actual value within this tolerance of the expected value. The
+/// tolerance is chosen to account for the inaccuracies in most ordinary
+/// floating point calculations. To see details of how the tolerance is
+/// calculated look at the implementation of
+/// [`googletest::approx_eq`][crate::matchers::approx_eq].
+///
+/// Evaluates to `Result::Ok(())` if the first argument is approximately equal
+/// to the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_float_eq!(1.0, 2.0)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_float_eq {
+    ($actual:expr, $expected:expr $(,)?) => {
+        verify_that!($actual, $crate::matchers::approx_eq($expected))
+    };
+}
+
+/// Marks test as failed and continues execution if the float given by the first
+/// argument is not approximately equal to the float given by the second
+/// argument.
+///
+/// This automatically computes a tolerance from the magnitude of `expected` and
+/// matches any actual value within this tolerance of the expected value. The
+/// tolerance is chosen to account for the inaccuracies in most ordinary
+/// floating point calculations. To see details of how the tolerance is
+/// calculated look at the implementation of
+/// [`googletest::approx_eq`][crate::matchers::approx_eq].
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_float_eq!(1.0, 2.0);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_float_eq!(1.0, 2.0, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_float_eq {
+    ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_float_eq!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_float_eq!($actual, $expected).and_log_failure();
+    }};
+}
+
+/// Checks whether the float given by first argument is equal to second argument
+/// with error tolerance of max_abs_error.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is approximately equal
+/// to the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+///  * Abort the current function with the `?` operator. This requires that the
+///    function return a suitable `Result`.
+///  * Log the test failure and continue by calling the method
+///    `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+///     verify_near!(1.12345, 1.12346, 1e-6)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_near {
+    ($actual:expr, $expected:expr, $max_abs_error:expr $(,)?) => {
+        verify_that!($actual, $crate::matchers::near($expected, $max_abs_error))
+    };
+}
+
+/// Marks the test as failed and continues execution if the float given by first
+/// argument is not equal to second argument with error tolerance of
+/// max_abs_error.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     expect_near!(1.12345, 1.12346, 1e-6);
+///     println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+///     let argument = "argument"
+///     expect_near!(1.12345, 1.12346, 1e-6, "custom failure message: {argument}");
+///     println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_near {
+    ($actual:expr, $expected:expr, $max_abs_error:expr, $($format_args:expr),+ $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_near!($actual, $expected, $max_abs_error)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure();
+    }};
+    ($actual:expr, $expected:expr, $max_abs_error:expr $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        verify_near!($actual, $expected, $max_abs_error).and_log_failure();
+    }};
+}
+
 /// Matches the given value against the given matcher, panicking if it does not
 /// match.
 ///
@@ -351,6 +1308,59 @@
 /// equivalent to `ASSERT_THAT`, use [`verify_that!`] with the `?` operator.
 #[macro_export]
 macro_rules! assert_that {
+    // specialized to sequence:
+    ($actual:expr, [ $($expected:expr),* ] $(,)?) => {
+        match $crate::verify_that!($actual, [ $($expected),* ]) {
+            Ok(_) => {}
+            Err(e) => {
+                // The extra newline before the assertion failure message makes the failure a
+                // bit easier to read when there's some generic boilerplate from the panic.
+                panic!("\n{}", e);
+            }
+        }
+    };
+
+    // specialized to unordered sequence
+    ($actual:expr, { $($expected:expr),* } $(,)?) => {
+        match $crate::verify_that!($actual, { $($expected),* }) {
+            Ok(_) => {}
+            Err(e) => {
+                // The extra newline before the assertion failure message makes the failure a
+                // bit easier to read when there's some generic boilerplate from the panic.
+                panic!("\n{}", e);
+            }
+        }
+    };
+
+    // w/ format args, specialized to sequence:
+    ($actual:expr, [ $($expected:expr),* ], $($format_args:expr),* $(,)?) => {
+        match $crate::verify_that!($actual, [ $($expected),* ])
+            .with_failure_message(|| format!($($format_args),*))
+        {
+            Ok(_) => {}
+            Err(e) => {
+                // The extra newline before the assertion failure message makes the failure a
+                // bit easier to read when there's some generic boilerplate from the panic.
+                panic!("\n{}", e);
+            }
+        }
+    };
+
+    // w/ format args, specialized to unordered sequence:
+    ($actual:expr, { $($expected:expr),* }, $($format_args:expr),* $(,)?) => {
+        match $crate::verify_that!($actual, { $($expected),* })
+            .with_failure_message(|| format!($($format_args),*))
+        {
+            Ok(_) => {}
+            Err(e) => {
+                // The extra newline before the assertion failure message makes the failure a
+                // bit easier to read when there's some generic boilerplate from the panic.
+                panic!("\n{}", e);
+            }
+        }
+    };
+
+    // general case:
     ($actual:expr, $expected:expr $(,)?) => {
         match $crate::verify_that!($actual, $expected) {
             Ok(_) => {}
@@ -362,6 +1372,7 @@
         }
     };
 
+    // w/ format args, general case:
     ($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {
         match $crate::verify_that!($actual, $expected)
             .with_failure_message(|| format!($($format_args),*))
@@ -405,7 +1416,7 @@
 /// execution in the event of assertion failure.
 ///
 /// This can only be invoked inside tests with the
-/// [`googletest::test`][crate::test] attribute. The assertion must
+/// [`gtest`][crate::gtest] attribute. The assertion must
 /// occur in the same thread as that running the test itself.
 ///
 /// Invoking this macro is equivalent to using
@@ -442,12 +1453,43 @@
 /// ```
 #[macro_export]
 macro_rules! expect_that {
+    // specialized to sequence:
+    ($actual:expr, [$($expected:expr),*] $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        $crate::verify_that!($actual, [$($expected),*]).and_log_failure();
+    }};
+
+    // specialized to unordered sequence:
+    ($actual:expr, {$($expected:expr),*} $(,)?) => {{
+        use $crate::GoogleTestSupport as _;
+        $crate::verify_that!($actual, {$($expected),*}).and_log_failure();
+    }};
+
+    // w/ format args, specialized to sequence:
+    ($actual:expr, [$($expected:expr),*], $($format_args:expr),* $(,)?) => {
+        use $crate::GoogleTestSupport as _;
+        $crate::verify_that!($actual, [$($expected),*])
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure()
+    };
+
+    // w/ format args, specialized to unordered sequence:
+    ($actual:expr, {$($expected:expr),*}, $($format_args:expr),* $(,)?) => {
+        use $crate::GoogleTestSupport as _;
+        $crate::verify_that!($actual, {$($expected),*})
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure()
+    };
+
+    // general case:
     ($actual:expr, $expected:expr $(,)?) => {{
-        use $crate::GoogleTestSupport;
+        use $crate::GoogleTestSupport as _;
         $crate::verify_that!($actual, $expected).and_log_failure();
     }};
 
+    // w/ format args, general case:
     ($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {
+        use $crate::GoogleTestSupport as _;
         $crate::verify_that!($actual, $expected)
             .with_failure_message(|| format!($($format_args),*))
             .and_log_failure()
@@ -461,7 +1503,7 @@
 /// continues execution in the event of assertion failure.
 ///
 /// This can only be invoked inside tests with the
-/// [`googletest::test`][crate::test] attribute. The assertion must
+/// [`gtest`][crate::gtest] attribute. The assertion must
 /// occur in the same thread as that running the test itself.
 ///
 /// Invoking this macro is equivalent to using
@@ -473,7 +1515,7 @@
 #[macro_export]
 macro_rules! expect_pred {
     ($($content:tt)*) => {{
-        use $crate::GoogleTestSupport;
+        use $crate::GoogleTestSupport as _;
         $crate::verify_pred!($($content)*).and_log_failure();
     }};
 }
@@ -484,50 +1526,142 @@
 #[doc(hidden)]
 pub mod internal {
     use crate::{
-        internal::{source_location::SourceLocation, test_outcome::TestAssertionFailure},
+        internal::test_outcome::TestAssertionFailure,
         matcher::{create_assertion_failure, Matcher, MatcherResult},
     };
     use std::fmt::Debug;
 
-    /// Checks whether the matcher `expected` matches the value `actual`, adding
-    /// a test failure report if it does not match.
-    ///
-    /// Returns `Ok(())` if the value matches and `Err(())` if it does not
-    /// match.
-    ///
-    /// **For internal use only. API stablility is not guaranteed!**
-    #[must_use = "The assertion result must be evaluated to affect the test result."]
-    pub fn check_matcher<T: Debug + ?Sized>(
-        actual: &T,
-        expected: impl Matcher<ActualT = T>,
-        actual_expr: &'static str,
-        source_location: SourceLocation,
-    ) -> Result<(), TestAssertionFailure> {
-        match expected.matches(actual) {
-            MatcherResult::Match => Ok(()),
-            MatcherResult::NoMatch => {
-                Err(create_assertion_failure(&expected, actual, actual_expr, source_location))
+    pub use ::googletest_macro::__googletest_macro_verify_pred;
+
+    /// Extension trait to perform autoref through method lookup in the
+    /// assertion macros. With this trait, the subject can be either a value
+    /// or a reference. For example, this trait makes the following code
+    /// compile and work:
+    /// ```
+    /// # use googletest::prelude::*;
+    /// # fn would_not_compile_without_autoref() -> Result<()> {
+    /// let not_copyable = vec![1,2,3];
+    /// verify_that!(not_copyable, empty())?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    /// See [Method Lookup](https://rustc-dev-guide.rust-lang.org/method-lookup.html)
+    pub trait Subject: Copy + Debug {
+        /// Checks whether the matcher `expected` matches the `Subject `self`,
+        /// adding a test failure report if it does not match.
+        ///
+        /// Returns `Ok(())` if the value matches and `Err(_)` if it does not
+        /// match.
+        ///
+        /// **For internal use only. API stablility is not guaranteed!**
+        #[must_use = "The assertion result must be evaluated to affect the test result."]
+        #[track_caller]
+        fn check(
+            self,
+            expected: impl Matcher<Self>,
+            actual_expr: &'static str,
+        ) -> Result<(), TestAssertionFailure> {
+            match expected.matches(self) {
+                MatcherResult::Match => Ok(()),
+                MatcherResult::NoMatch => {
+                    Err(create_assertion_failure(&expected, self, actual_expr))
+                }
             }
         }
     }
 
-    /// Constructs a `Result::Err(TestAssertionFailure)` for a predicate failure
-    /// as produced by the macro [`crate::verify_pred`].
+    impl<T: Copy + Debug> Subject for T {}
+
+    /// Creates a failure at specified location.
     ///
-    /// This intended only for use by the macro [`crate::verify_pred`].
-    ///
-    /// **For internal use only. API stablility is not guaranteed!**
+    /// **For internal use only. API stability is not guaranteed!**
     #[must_use = "The assertion result must be evaluated to affect the test result."]
-    pub fn report_failed_predicate(
-        actual_expr: &'static str,
-        formatted_arguments: Vec<String>,
-        source_location: SourceLocation,
-    ) -> Result<(), TestAssertionFailure> {
-        Err(TestAssertionFailure::create(format!(
-            "{} was false with\n  {}\n{}",
-            actual_expr,
-            formatted_arguments.join(",\n  "),
-            source_location,
-        )))
+    #[track_caller]
+    pub fn create_fail_result(message: String) -> crate::Result<()> {
+        Err(crate::internal::test_outcome::TestAssertionFailure::create(message))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::prelude::*;
+
+    #[test]
+    fn verify_of_hash_maps_with_str_string_matching() -> Result<()> {
+        let hash_map: std::collections::HashMap<String, String> =
+            std::collections::HashMap::from([("a".into(), "A".into()), ("b".into(), "B".into())]);
+        verify_eq!(hash_map, {("a", "A"), ("b", "B")})
+    }
+
+    #[test]
+    fn verify_of_hash_maps_with_ad_hoc_struct() -> Result<()> {
+        #[derive(PartialEq, Debug)]
+        struct Greek(String);
+
+        let hash_map: std::collections::HashMap<String, Greek> = std::collections::HashMap::from([
+            ("a".into(), Greek("Alpha".into())),
+            ("b".into(), Greek("Beta".into())),
+        ]);
+        verify_eq!(hash_map, {
+            ("b", Greek("Beta".into())),
+            ("a", Greek("Alpha".into())),
+        })
+    }
+
+    #[test]
+    fn verify_of_hash_maps_with_i32s() -> Result<()> {
+        let hash_map: std::collections::HashMap<i32, i32> =
+            std::collections::HashMap::from([(1, 1), (2, 4), (-1, 1), (-3, 9)]);
+        verify_eq!(hash_map, {
+            (-3, 9),
+            (-1, 1),
+            (1, 1),
+            (2, 4),
+        })
+    }
+
+    #[test]
+    fn verify_eq_of_unordered_pairs() -> Result<()> {
+        verify_eq!(vec![(1, 2), (2, 3)], {(1, 2), (2, 3)})?;
+        verify_eq!(vec![(1, 2), (2, 3)], {(2, 3), (1, 2)})
+    }
+
+    #[test]
+    fn verify_eq_of_unordered_structs() -> Result<()> {
+        #[derive(PartialEq, Debug)]
+        struct P(i32, i32);
+
+        verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)],
+                  {P(1, 1), P(1, 2), P(3, 7)})?;
+        verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)],
+                  {P(3,7), P(1, 1), P(1, 2)})
+    }
+
+    #[test]
+    fn verify_eq_of_ordered_pairs() -> Result<()> {
+        verify_eq!(vec![(1, 2), (2, 3)], [(1, 2), (2, 3)])
+    }
+
+    #[test]
+    fn verify_eq_of_ordered_structs() -> Result<()> {
+        #[derive(PartialEq, Debug)]
+        struct P(i32, i32);
+
+        verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)], [P(1, 1), P(1, 2), P(3, 7)])
+    }
+
+    #[test]
+    fn verify_eq_of_ordered_pairs_order_matters() -> Result<()> {
+        let result = verify_eq!(vec![(1, 2), (2, 3)], [(2, 3), (1, 2)]);
+        verify_that!(result, err(anything()))
+    }
+
+    #[test]
+    fn verify_eq_of_ordered_structs_order_matters() -> Result<()> {
+        #[derive(PartialEq, Debug)]
+        struct P(i32, i32);
+
+        let result = verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)], [P(3, 7), P(1, 1), P(1, 2)]);
+        verify_that!(result, err(anything()))
     }
 }
diff --git a/crates/googletest/src/description.rs b/crates/googletest/src/description.rs
index 9605559..e7339c1 100644
--- a/crates/googletest/src/description.rs
+++ b/crates/googletest/src/description.rs
@@ -89,6 +89,8 @@
 pub struct Description {
     elements: List,
     initial_indentation: usize,
+    is_conjunction: bool,
+    is_disjunction: bool,
 }
 
 impl Description {
@@ -199,6 +201,27 @@
     pub fn is_empty(&self) -> bool {
         self.elements.is_empty()
     }
+
+    pub(crate) fn push_in_last_nested(mut self, inner: Description) -> Self {
+        self.elements.push_at_end(inner.elements);
+        self
+    }
+
+    pub(crate) fn conjunction_description(self) -> Self {
+        Self { is_conjunction: true, ..self }
+    }
+
+    pub(crate) fn is_conjunction_description(&self) -> bool {
+        self.is_conjunction
+    }
+
+    pub(crate) fn disjunction_description(self) -> Self {
+        Self { is_disjunction: true, ..self }
+    }
+
+    pub(crate) fn is_disjunction_description(&self) -> bool {
+        self.is_disjunction
+    }
 }
 
 impl Display for Description {
@@ -359,4 +382,24 @@
             )))
         )
     }
+
+    #[test]
+    fn new_is_empty() -> Result<()> {
+        verify_that!(Description::new(), predicate(Description::is_empty))
+    }
+
+    #[test]
+    fn text_is_not_empty() -> Result<()> {
+        verify_that!(Description::new().text("something"), not(predicate(Description::is_empty)))
+    }
+
+    #[test]
+    fn new_zero_length() -> Result<()> {
+        verify_that!(Description::new().len(), eq(0))
+    }
+
+    #[test]
+    fn text_one_length() -> Result<()> {
+        verify_that!(Description::new().text("something").len(), eq(1))
+    }
 }
diff --git a/crates/googletest/src/fixtures.rs b/crates/googletest/src/fixtures.rs
new file mode 100644
index 0000000..27fc7c2
--- /dev/null
+++ b/crates/googletest/src/fixtures.rs
@@ -0,0 +1,261 @@
+// Copyright 2022 Google LLC
+//
+// 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.
+
+use std::{
+    any::{Any, TypeId},
+    collections::HashMap,
+    ops::{Deref, DerefMut},
+    sync::{Mutex, OnceLock},
+};
+
+/// Interface for structure to be set up and torn down as part of a test.
+/// Types implementing `Fixture` can be passed as a reference argument to a
+/// test function.
+///
+/// ```ignore
+/// strct MyFixture { ... }
+///
+/// impl Fixture for MyFixture { ... }
+///
+/// #[gtest]
+/// fn test_with_fixture(my_fixture: &MyFixture) {...}
+/// ```
+pub trait Fixture: Sized {
+    /// Factory method of the `Fixture`.
+    ///
+    /// This method is called by the test harness before the test case
+    /// If this method returns an `Err(...)`, then the test case is not
+    /// evaluated and only the fixtures previously set up are torn down.
+    fn set_up() -> crate::Result<Self>;
+
+    /// Clean up method for the fixture.
+    ///
+    /// This method is called by the test harness after the test case.
+    /// If the `Fixture` has been set up, the test harness will call this
+    /// method, even if the test case failed or panicked.
+    fn tear_down(self) -> crate::Result<()>;
+}
+
+/// Interface for structure to be set up before the test case.
+/// Types implementing `ConsumableFixture` can be passed by value to
+/// a test function.
+///
+/// ```ignore
+/// strct MyFixture { ... }
+///
+/// impl ConsumableFixture for MyFixture { ... }
+///
+/// #[gtest]
+/// fn test_with_fixture(my_fixture: MyFixture) {...}
+/// ```
+pub trait ConsumableFixture: Sized {
+    /// Factory method of the `ConsumableFixture`.
+    ///
+    /// This method is called by the test harness before the test case.
+    /// If this method returns an `Err(...)`, then the test case is not
+    /// evaluated.
+    fn set_up() -> crate::Result<Self>;
+}
+
+/// Generic adapter to implement `ConsumableFixture` on any type implementing
+/// `Default`.
+pub struct FixtureOf<T>(T);
+
+impl<T: Default> ConsumableFixture for FixtureOf<T> {
+    fn set_up() -> crate::Result<Self> {
+        Ok(Self(T::default()))
+    }
+}
+
+impl<T> Deref for FixtureOf<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for FixtureOf<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+/// Interface for structure to be set up only once before all tests.
+/// Types implementing `StaticFixture` can be passed as a double referenced
+/// argument to a test function.
+///
+/// ```ignore
+/// strct MyFixture{ ... }
+///
+/// impl StaticFixture for MyFixture { ... }
+///
+/// #[gtest]
+/// fn test_with_fixture(my_fixture: &&MyFixture){...}
+/// ```
+pub trait StaticFixture: Sized + Sync + Send {
+    /// Factory method of the `StaticFixture`.
+    ///
+    /// This method is called by the test harness before the first test case
+    /// using this fixture. If this method returns an `Err(...)`, then every
+    /// test cases using this fixture are not evaluated.
+    fn set_up_once() -> crate::Result<Self>;
+}
+
+impl<F: StaticFixture + 'static> Fixture for &'static F {
+    fn set_up() -> crate::Result<Self> {
+        static ONCE_FIXTURE_REPO: OnceLock<
+            Mutex<HashMap<TypeId, &'static (dyn Any + Sync + Send)>>,
+        > = OnceLock::new();
+        let mut map = ONCE_FIXTURE_REPO.get_or_init(|| Mutex::new(HashMap::new())).lock()?;
+        let any =
+            map.entry(TypeId::of::<F>()).or_insert_with(|| Box::leak(Box::new(F::set_up_once())));
+        match any.downcast_ref::<crate::Result<F>>() {
+            Some(Ok(ref fixture)) => Ok(fixture),
+            Some(Err(e)) => Err(e.clone()),
+            None => panic!("Downcast failed. This is a bug in GoogleTest Rust"),
+        }
+    }
+
+    // Note that this is `&F` being torn down, not `F`.
+    fn tear_down(self) -> crate::Result<()> {
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use std::sync::Once;
+
+    use super::FixtureOf;
+    use super::StaticFixture;
+    use crate as googletest;
+    use crate::prelude::*;
+    use crate::test;
+
+    #[test]
+    fn fixture_no_fixture() -> Result<()> {
+        Ok(())
+    }
+
+    struct AlwaysSucceed;
+
+    impl Fixture for AlwaysSucceed {
+        fn set_up() -> crate::Result<Self> {
+            Ok(Self)
+        }
+
+        fn tear_down(self) -> crate::Result<()> {
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn fixture_one_fixture(_: &AlwaysSucceed) -> Result<()> {
+        Ok(())
+    }
+
+    #[test]
+    fn fixture_three_fixtures(
+        _: &AlwaysSucceed,
+        _: &AlwaysSucceed,
+        _: &AlwaysSucceed,
+    ) -> Result<()> {
+        Ok(())
+    }
+
+    struct NotAFixture {
+        a_field: i32,
+    }
+
+    impl Default for NotAFixture {
+        fn default() -> Self {
+            Self { a_field: 33 }
+        }
+    }
+
+    #[test]
+    fn fixture_of_non_fixture(not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
+        verify_that!(not_a_fixture.a_field, eq(33))
+    }
+
+    #[test]
+    fn fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
+        not_a_fixture.a_field += 10;
+        verify_that!(not_a_fixture.a_field, eq(43))
+    }
+    struct PanickyFixture;
+
+    impl Fixture for PanickyFixture {
+        fn set_up() -> crate::Result<Self> {
+            Ok(Self)
+        }
+
+        fn tear_down(self) -> crate::Result<()> {
+            panic!("Whoooops");
+        }
+    }
+
+    #[test]
+    #[should_panic(expected = "Whoooops")]
+    fn fixture_teardown_called_even_if_test_fail(_: &PanickyFixture) {
+        panic!("Test failed");
+    }
+
+    struct FailingTearDown;
+
+    impl Fixture for FailingTearDown {
+        fn set_up() -> crate::Result<Self> {
+            Ok(Self)
+        }
+
+        fn tear_down(self) -> crate::Result<()> {
+            Err(googletest::TestAssertionFailure::create("It must fail!".into()))
+        }
+    }
+
+    struct OnlyOnce;
+
+    impl StaticFixture for OnlyOnce {
+        fn set_up_once() -> crate::Result<Self> {
+            static ONCE: Once = Once::new();
+            assert!(!ONCE.is_completed());
+            ONCE.call_once(|| {});
+            Ok(Self)
+        }
+    }
+
+    #[test]
+    fn static_fixture_works(_: &&OnlyOnce) {}
+
+    #[test]
+    fn static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce) {
+        // checks it points to the same memory address.
+        let once: *const OnlyOnce = *once;
+        let twice: *const OnlyOnce = *twice;
+        expect_eq!(once, twice);
+    }
+
+    struct AnotherStaticFixture;
+
+    impl StaticFixture for AnotherStaticFixture {
+        fn set_up_once() -> crate::Result<Self> {
+            Ok(Self)
+        }
+    }
+
+    #[test]
+    fn static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture) {}
+}
diff --git a/crates/googletest/src/fmt.rs b/crates/googletest/src/fmt.rs
new file mode 100644
index 0000000..267b127
--- /dev/null
+++ b/crates/googletest/src/fmt.rs
@@ -0,0 +1,67 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+/// Functions for use only by the procedural macros in this module.
+///
+/// **For internal use only. API stablility is not guaranteed!**
+#[doc(hidden)]
+pub mod internal {
+    use std::fmt::{Debug, Write};
+
+    /// Wrapper to allow for inherent-method specialization based on whether a
+    /// type implements `Debug`.
+    pub struct FormatWrapper<'a, T: ?Sized>(pub &'a T);
+
+    /// Default implementation to render values that implement `Debug`.
+    ///
+    /// Used for autoref specialization to conditionally
+    /// render only values that implement `Debug`. See also
+    /// [`FormatNonDebugFallback`].
+    impl<'a, T: Debug + ?Sized> FormatWrapper<'a, T> {
+        #[track_caller]
+        pub fn __googletest_write_expr_value(&self, output: &mut dyn Write, expr_label: &str) {
+            write!(output, "\n  {} = {:?},", expr_label, self.0)
+                .expect("Formatting to String should never fail");
+        }
+    }
+
+    /// Fallback implementation for rendering values for non-`Debug` types..
+    ///
+    /// Used for inherent-method specialization to conditionally render only
+    /// values that implement `Debug`. See also the specialized inherent impl on
+    /// [`FormatWrapper`] above.
+    pub trait FormatNonDebugFallback {
+        fn __googletest_write_expr_value(&self, output: &mut dyn Write, expr_label: &str);
+    }
+
+    impl<'a, T: ?Sized> FormatNonDebugFallback for FormatWrapper<'a, T> {
+        #[track_caller]
+        fn __googletest_write_expr_value(&self, output: &mut dyn Write, expr_label: &str) {
+            write!(output, "\n  {} does not implement Debug,", expr_label)
+                .expect("Formatting to String should never fail");
+        }
+    }
+
+    #[macro_export]
+    macro_rules! __googletest__write_expr_value(
+        ($output:expr, $expr_str:expr, $value:expr $(,)?) => {
+            {
+                use $crate::fmt::internal::FormatNonDebugFallback as _;
+                $crate::fmt::internal::FormatWrapper(&$value)
+                    .__googletest_write_expr_value(&mut $output, $expr_str)
+            }
+        }
+    );
+    pub use __googletest__write_expr_value;
+}
diff --git a/crates/googletest/src/internal/description_renderer.rs b/crates/googletest/src/internal/description_renderer.rs
index 937f0d5..e84fabe 100644
--- a/crates/googletest/src/internal/description_renderer.rs
+++ b/crates/googletest/src/internal/description_renderer.rs
@@ -83,6 +83,17 @@
         self.0.is_empty()
     }
 
+    /// Append a new [`List`] in the last element which must be a
+    /// [`Block::Nested`]. Panic if `self` is empty or the last element is
+    /// not [`Block::Nested`].
+    pub(crate) fn push_at_end(&mut self, list: List) {
+        if let Some(Block::Nested(self_list)) = self.0.last_mut() {
+            self_list.push_nested(list);
+        } else {
+            panic!("pushing elements at the end of {self:#?} which last element is not Nested")
+        }
+    }
+
     fn render_with_prefix(
         &self,
         f: &mut dyn Write,
diff --git a/crates/googletest/src/internal/mod.rs b/crates/googletest/src/internal/mod.rs
index 9bccdc3..1269bc9 100644
--- a/crates/googletest/src/internal/mod.rs
+++ b/crates/googletest/src/internal/mod.rs
@@ -15,5 +15,4 @@
 #![doc(hidden)]
 
 pub(crate) mod description_renderer;
-pub mod source_location;
 pub mod test_outcome;
diff --git a/crates/googletest/src/internal/source_location.rs b/crates/googletest/src/internal/source_location.rs
deleted file mode 100644
index 4520c92..0000000
--- a/crates/googletest/src/internal/source_location.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// 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.
-
-use std::fmt::{Display, Error, Formatter};
-
-/// Encapsulates a location in source code.
-///
-/// This is intended to report the location of an assertion which failed to
-/// stdout.
-///
-/// **For internal use only. API stablility is not guaranteed!**
-#[doc(hidden)]
-pub struct SourceLocation {
-    file: &'static str,
-    line: u32,
-    column: u32,
-}
-
-impl SourceLocation {
-    /// Constructs a new [`SourceLocation`].
-    ///
-    /// **For internal use only. API stablility is not guaranteed!**
-    #[doc(hidden)]
-    pub fn new(file: &'static str, line: u32, column: u32) -> Self {
-        Self { file, line, column }
-    }
-}
-
-impl Display for SourceLocation {
-    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
-        write!(f, "  at {}:{}:{}", self.file, self.line, self.column)
-    }
-}
diff --git a/crates/googletest/src/internal/test_outcome.rs b/crates/googletest/src/internal/test_outcome.rs
index 132ba99..edc8d2b 100644
--- a/crates/googletest/src/internal/test_outcome.rs
+++ b/crates/googletest/src/internal/test_outcome.rs
@@ -32,14 +32,13 @@
 }
 
 thread_local! {
-    static CURRENT_TEST_OUTCOME: RefCell<Option<TestOutcome>> = RefCell::new(None);
+    static CURRENT_TEST_OUTCOME: RefCell<Option<TestOutcome>> = const { RefCell::new(None) };
 }
 
 impl TestOutcome {
     /// Resets the current test's [`TestOutcome`].
     ///
-    /// This is intended only for use by the attribute macro
-    /// `#[googletest::test]`.
+    /// This is intended only for use by the attribute macro `#[gtest]`.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
@@ -86,6 +85,7 @@
 
     /// Returns a `Result` corresponding to the outcome of the currently running
     /// test.
+    #[track_caller]
     pub(crate) fn get_current_test_outcome() -> Result<(), TestAssertionFailure> {
         TestOutcome::with_current_test_outcome(|mut outcome| {
             let outcome = outcome
@@ -117,12 +117,12 @@
     }
 
     /// Ensure that there is a test context present and panic if there is not.
-    pub(crate) fn ensure_text_context_present() {
+    pub(crate) fn ensure_test_context_present() {
         TestOutcome::with_current_test_outcome(|outcome| {
             outcome.as_ref().expect(
                 "
 No test context found.
- * Did you annotate the test with googletest::test?
+ * Did you annotate the test with gtest?
  * Is the assertion running in the original test thread?
 ",
             );
@@ -162,14 +162,50 @@
     /// A human-readable formatted string describing the error.
     pub description: String,
     pub custom_message: Option<String>,
+    location: Location,
+}
+
+/// A code location.
+///
+/// `std::panic::Location` does not provide a constructor, hence we cannot
+/// construct a fake value.
+///
+/// **For internal use only. API stablility is not guaranteed!**
+#[doc(hidden)]
+#[derive(Clone)]
+enum Location {
+    Real(&'static std::panic::Location<'static>),
+    Fake { file: &'static str, line: u32, column: u32 },
+}
+
+impl Display for Location {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Location::Real(l) => write!(f, "{}", l),
+            Location::Fake { file, line, column } => write!(f, "{}:{}:{}", file, line, column),
+        }
+    }
 }
 
 impl TestAssertionFailure {
     /// Creates a new instance with the given `description`.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
+    #[track_caller]
     pub fn create(description: String) -> Self {
-        Self { description, custom_message: None }
+        Self {
+            description,
+            custom_message: None,
+            location: Location::Real(std::panic::Location::caller()),
+        }
+    }
+
+    /// Set `location`` to a fake value.
+    ///
+    /// **For internal use only. API stablility is not guaranteed!**
+    pub fn with_fake_location(mut self, file: &'static str, line: u32, column: u32) -> Self {
+        self.location = Location::Fake { file, line, column };
+        self
     }
 
     pub(crate) fn log(&self) {
@@ -184,7 +220,7 @@
         if let Some(custom_message) = &self.custom_message {
             writeln!(f, "{}", custom_message)?;
         }
-        Ok(())
+        writeln!(f, "  at {}", self.location)
     }
 }
 
@@ -198,6 +234,7 @@
 }
 
 impl<T: std::error::Error> From<T> for TestAssertionFailure {
+    #[track_caller]
     fn from(value: T) -> Self {
         TestAssertionFailure::create(format!("{value}"))
     }
diff --git a/crates/googletest/src/lib.rs b/crates/googletest/src/lib.rs
index 51b6345..3f59546 100644
--- a/crates/googletest/src/lib.rs
+++ b/crates/googletest/src/lib.rs
@@ -22,11 +22,16 @@
 #[macro_use]
 pub mod assertions;
 pub mod description;
+pub mod fixtures;
+#[macro_use]
+pub mod fmt;
 pub mod internal;
 pub mod matcher;
 pub mod matcher_support;
 pub mod matchers;
 
+pub use googletest_macro::{__abbreviated_stringify, __googletest_macro_verify_pred};
+
 /// Re-exports of the symbols in this crate which are most likely to be used.
 ///
 /// This includes:
@@ -42,16 +47,25 @@
 /// }
 /// ```
 pub mod prelude {
-    pub use super::matcher::Matcher;
+    pub use super::fixtures::{ConsumableFixture, Fixture, FixtureOf, StaticFixture};
+    pub use super::gtest;
+    pub use super::matcher::{Matcher, MatcherBase};
     pub use super::matchers::*;
     pub use super::verify_current_test_outcome;
     pub use super::GoogleTestSupport;
     pub use super::IntoTestResult;
     pub use super::Result;
     // Assert macros
-    pub use super::{assert_that, expect_pred, expect_that, fail, verify_pred, verify_that};
+    pub use super::{
+        add_failure, add_failure_at, assert_pred, assert_that, expect_eq, expect_false,
+        expect_float_eq, expect_ge, expect_gt, expect_le, expect_lt, expect_ne, expect_near,
+        expect_pred, expect_that, expect_true, fail, succeed, verify_eq, verify_false,
+        verify_float_eq, verify_ge, verify_gt, verify_le, verify_lt, verify_ne, verify_near,
+        verify_pred, verify_that, verify_true,
+    };
 }
 
+pub use googletest_macro::gtest;
 pub use googletest_macro::test;
 
 use internal::test_outcome::{TestAssertionFailure, TestOutcome};
@@ -89,13 +103,12 @@
 /// `?` operator to continue execution of the test conditionally on there not
 /// having been any failure yet.
 ///
-/// This requires the use of the [`#[googletest::test]`][crate::test] attribute
-/// macro.
+/// This requires the use of the [`#[gtest]`][crate::gtest] attribute macro.
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # /* Make sure this also compiles as a doctest.
-/// #[googletest::test]
+/// #[gtest]
 /// # */
 /// # fn foo() -> u32 { 1 }
 /// # fn bar() -> u32 { 2 }
@@ -108,6 +121,7 @@
 /// }
 /// # verify_that!(should_fail_and_not_execute_last_assertion(), err(displays_as(contains_substring("Test failed")))).unwrap();
 /// ```
+#[track_caller]
 pub fn verify_current_test_outcome() -> Result<()> {
     TestOutcome::get_current_test_outcome()
 }
@@ -203,7 +217,7 @@
 
 impl<T> GoogleTestSupport for std::result::Result<T, TestAssertionFailure> {
     fn and_log_failure(self) {
-        TestOutcome::ensure_text_context_present();
+        TestOutcome::ensure_test_context_present();
         if let Err(failure) = self {
             failure.log();
         }
@@ -229,44 +243,52 @@
 ///
 /// A type can implement this trait to provide an easy way to return immediately
 /// from a test in conjunction with the `?` operator. This is useful for
-/// [`Result`][std::result::Result] types whose `Result::Err` variant does not
-/// implement [`std::error::Error`].
+/// [`Option`] and [`Result`][std::result::Result] types whose `Result::Err`
+/// variant does not implement [`std::error::Error`].
 ///
-/// There is an implementation of this trait for [`anyhow::Error`] (which does
-/// not implement `std::error::Error`) when the `anyhow` feature is enabled.
-/// Importing this trait allows one to easily map [`anyhow::Error`] to a test
-/// failure:
+/// If `Result::Err` implements [`std::error::Error`] you can just use the `?`
+/// operator directly.
 ///
 /// ```ignore
 /// #[test]
-/// fn should_work() -> Result<()> {
+/// fn should_work() -> googletest::Result<()> {
 ///     let value = something_which_can_fail().into_test_result()?;
+///     let value = something_which_can_fail_with_option().into_test_result()?;
 ///     ...
 /// }
 ///
-/// fn something_which_can_fail() -> anyhow::Result<...> { ... }
+/// fn something_which_can_fail() -> std::result::Result<T, String> { ... }
+/// fn something_which_can_fail_with_option() -> Option<T> { ... }
 /// ```
 pub trait IntoTestResult<T> {
     /// Converts this instance into a [`Result`].
     ///
-    /// Typically, the `Self` type is itself a [`std::result::Result`]. This
-    /// method should then map the `Err` variant to a [`TestAssertionFailure`]
-    /// and leave the `Ok` variant unchanged.
+    /// Typically, the `Self` type is itself an implementation of the
+    /// [`std::ops::Try`] trait. This method should then map the `Residual`
+    /// variant to a [`TestAssertionFailure`] and leave the `Output` variant
+    /// unchanged.
     fn into_test_result(self) -> Result<T>;
 }
 
-#[cfg(feature = "anyhow")]
-impl<T> IntoTestResult<T> for std::result::Result<T, anyhow::Error> {
+impl<T, E: std::fmt::Debug> IntoTestResult<T> for std::result::Result<T, E> {
+    #[track_caller]
     fn into_test_result(self) -> std::result::Result<T, TestAssertionFailure> {
-        self.map_err(|e| TestAssertionFailure::create(format!("{e}")))
+        match self {
+            Ok(t) => Ok(t),
+            Err(e) => Err(TestAssertionFailure::create(format!("{e:?}"))),
+        }
     }
 }
 
-#[cfg(feature = "proptest")]
-impl<OkT, CaseT: std::fmt::Debug> IntoTestResult<OkT>
-    for std::result::Result<OkT, proptest::test_runner::TestError<CaseT>>
-{
-    fn into_test_result(self) -> std::result::Result<OkT, TestAssertionFailure> {
-        self.map_err(|e| TestAssertionFailure::create(format!("{e}")))
+impl<T> IntoTestResult<T> for Option<T> {
+    #[track_caller]
+    fn into_test_result(self) -> std::result::Result<T, TestAssertionFailure> {
+        match self {
+            Some(t) => Ok(t),
+            None => Err(TestAssertionFailure::create(format!(
+                "called `Option::into_test_result()` on a `Option::<{}>::None` value",
+                std::any::type_name::<T>()
+            ))),
+        }
     }
 }
diff --git a/crates/googletest/src/matcher.rs b/crates/googletest/src/matcher.rs
index 071b0d1..948ea41 100644
--- a/crates/googletest/src/matcher.rs
+++ b/crates/googletest/src/matcher.rs
@@ -15,24 +15,35 @@
 //! The components required to implement matchers.
 
 use crate::description::Description;
-use crate::internal::source_location::SourceLocation;
 use crate::internal::test_outcome::TestAssertionFailure;
 use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher;
 use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher;
+pub use googletest_macro::MatcherBase;
 use std::fmt::Debug;
 
 /// An interface for checking an arbitrary condition on a datum.
-pub trait Matcher {
-    /// The type against which this matcher matches.
-    type ActualT: Debug + ?Sized;
+///
+/// This trait is automatically implemented for a reference of any type
+/// implementing `Matcher`. This simplifies reusing a matcher in different
+/// assertions.
+///
+/// It is also implemented for tuple of `Matcher`. If `MatcherT: Matcher<T>` and
+/// `MatcherU: Matcher<U>`, then `(MatcherT, MatcherU): Matcher<(T, U)>`, and so
+/// on, up to 12 elements. Tuples longer than that do not automatically inherit
+/// the `Debug` trait from their members, so are generally not well-supported;
+/// see [Rust by Example](https://doc.rust-lang.org/rust-by-example/primitives/tuples.html#tuples).
 
+// `ActualT` requires `Copy` so that `actual` could be passed to `matches` and
+// if it fails passed to `explain_match`. We can relax this constraint later by
+// requiring only `Clone`.
+pub trait Matcher<ActualT: Debug + Copy>: MatcherBase {
     /// Returns whether the condition matches the datum `actual`.
     ///
     /// The trait implementation defines what it means to "match". Often the
     /// matching condition is based on data stored in the matcher. For example,
     /// `eq` matches when its stored expected value is equal (in the sense of
     /// the `==` operator) to the value `actual`.
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult;
+    fn matches(&self, actual: ActualT) -> MatcherResult;
 
     /// Returns a description of `self` or a negative description if
     /// `matcher_result` is `DoesNotMatch`.
@@ -117,8 +128,8 @@
     /// inner matcher and appears as follows:
     ///
     /// ```ignore
-    /// fn explain_match(&self, actual: &Self::ActualT) -> Description {
-    ///     self.expected.explain_match(actual.deref())
+    /// fn explain_match(&self, actual: &ActualT) -> Description {
+    ///     self.expected.explain_match(*actual)
     /// }
     /// ```
     ///
@@ -127,16 +138,30 @@
     /// inner matcher at a point where a relative clause would fit. For example:
     ///
     /// ```ignore
-    /// fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    /// fn explain_match(&self, actual: ActualT) -> Description {
     ///     Description::new()
     ///         .text("which points to a value")
-    ///         .nested(self.expected.explain_match(actual.deref()))
+    ///         .nested(self.expected.explain_match(*actual))
     /// }
     /// ```
-    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    fn explain_match(&self, actual: ActualT) -> Description {
         format!("which {}", self.describe(self.matches(actual))).into()
     }
+}
 
+/// Base trait for matchers. Any type implementing `Matcher` must implement
+/// `MatcherBase`, but that should be done through the `#[derive(MatcherBase)]`
+/// macro.
+// The `and` and `or` functions cannot be part of the `Matcher` trait since it
+// can be implemented multiple times for a given matcher type. Consider that
+// `and` and `or` are part of the `Matcher` trait and `MyMatcher` implements
+// both `Matcher<A>` and `Matcher<B>`. Then `MyMatcher{...}.and(...)` can be
+// either:
+//   * `Matcher::<A>::and(MyMatcher{...}, ...)` or
+//   * `Matcher::<B>::and(MyMatcher{...}, ...)`.
+// Moving the `and` and `or` functions in a non-generic trait removes this
+// confusion by making `and` and `or` unique for a given type.
+pub trait MatcherBase {
     /// Constructs a matcher that matches both `self` and `right`.
     ///
     /// ```
@@ -160,10 +185,7 @@
     // TODO(b/264518763): Replace the return type with impl Matcher and reduce
     // visibility of ConjunctionMatcher once impl in return position in trait
     // methods is stable.
-    fn and<Right: Matcher<ActualT = Self::ActualT>>(
-        self,
-        right: Right,
-    ) -> ConjunctionMatcher<Self, Right>
+    fn and<Right>(self, right: Right) -> ConjunctionMatcher<Self, Right>
     where
         Self: Sized,
     {
@@ -190,10 +212,7 @@
     // TODO(b/264518763): Replace the return type with impl Matcher and reduce
     // visibility of DisjunctionMatcher once impl in return position in trait
     // methods is stable.
-    fn or<Right: Matcher<ActualT = Self::ActualT>>(
-        self,
-        right: Right,
-    ) -> DisjunctionMatcher<Self, Right>
+    fn or<Right>(self, right: Right) -> DisjunctionMatcher<Self, Right>
     where
         Self: Sized,
     {
@@ -210,11 +229,11 @@
 ///
 /// The parameter `actual_expr` contains the expression which was evaluated to
 /// obtain `actual`.
-pub(crate) fn create_assertion_failure<T: Debug + ?Sized>(
-    matcher: &impl Matcher<ActualT = T>,
-    actual: &T,
+#[track_caller]
+pub(crate) fn create_assertion_failure<T: Debug + Copy>(
+    matcher: &impl Matcher<T>,
+    actual: T,
     actual_expr: &'static str,
-    source_location: SourceLocation,
 ) -> TestAssertionFailure {
     let actual_formatted = format!("{actual:?}");
     let actual_formatted = if actual_formatted.len() > PRETTY_PRINT_LENGTH_THRESHOLD {
@@ -227,8 +246,7 @@
 Value of: {actual_expr}
 Expected: {}
 Actual: {actual_formatted},
-{}
-{source_location}",
+{}",
         matcher.describe(MatcherResult::Match),
         matcher.explain_match(actual).indent(),
     ))
@@ -245,7 +263,11 @@
 
 impl From<bool> for MatcherResult {
     fn from(b: bool) -> Self {
-        if b { MatcherResult::Match } else { MatcherResult::NoMatch }
+        if b {
+            MatcherResult::Match
+        } else {
+            MatcherResult::NoMatch
+        }
     }
 }
 
@@ -268,3 +290,39 @@
         matches!(self, MatcherResult::NoMatch)
     }
 }
+
+impl<M: ?Sized + MatcherBase> MatcherBase for &M {}
+
+impl<T: Debug + Copy, M: Matcher<T>> Matcher<T> for &M {
+    fn matches(&self, actual: T) -> MatcherResult {
+        (*self).matches(actual)
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        (*self).describe(matcher_result)
+    }
+
+    fn explain_match(&self, actual: T) -> Description {
+        (*self).explain_match(actual)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::prelude::*;
+
+    #[test]
+    fn ref_matchers_can_be_reused() -> Result<()> {
+        let matcher = eq(1);
+
+        verify_that!(1, &matcher)?;
+        verify_that!(1, &matcher)
+    }
+
+    #[test]
+    fn ref_matchers_as_inner_matcher() -> Result<()> {
+        let matcher = gt(1);
+
+        verify_that!([2, 3, 4, 5], [&matcher, &matcher, &matcher, &matcher])
+    }
+}
diff --git a/crates/googletest/src/matcher_support/auto_eq.rs b/crates/googletest/src/matcher_support/auto_eq.rs
new file mode 100644
index 0000000..16bfde3
--- /dev/null
+++ b/crates/googletest/src/matcher_support/auto_eq.rs
@@ -0,0 +1,106 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+#![doc(hidden)]
+
+/// Macro that wraps the expression with `eq(...)` if the expression is
+/// not a matcher.
+///
+/// This is useful to let users pass expected value to macro matchers like
+/// `field!` and `property!`.
+///`
+/// **For internal use only. API stablility is not guaranteed!**
+/// If you are interested in using it in your matcher, please file an issue to
+/// stabilize this.
+#[macro_export]
+macro_rules! __auto_eq {
+    ($e:expr) => {{
+        #[allow(unused_imports)]
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::ExpectedKind as _;
+        match $e {
+            expected => {
+                $crate::matcher_support::__internal_unstable_do_not_depend_on_these::Wrapper(
+                    &expected,
+                )
+                .kind()
+                .matcher(expected)
+            }
+        }
+    }};
+}
+
+// This reimplements the pattern presented in
+// https://github.com/dtolnay/case-studies/issues/14
+pub mod internal {
+    use crate::{
+        matcher::MatcherBase,
+        matchers::{eq, EqMatcher},
+    };
+
+    pub struct Wrapper<T>(pub T);
+
+    impl<'a, T: MatcherBase> Wrapper<&'a T> {
+        #[inline]
+        pub fn kind(&self) -> MatcherTag {
+            MatcherTag
+        }
+    }
+
+    pub trait ExpectedKind {
+        #[inline]
+        fn kind(&self) -> ExpectedTag {
+            ExpectedTag
+        }
+    }
+
+    impl<T> ExpectedKind for Wrapper<T> {}
+
+    pub struct MatcherTag;
+
+    impl MatcherTag {
+        #[inline]
+        pub fn matcher<M>(self, matcher: M) -> M {
+            matcher
+        }
+    }
+    pub struct ExpectedTag;
+
+    impl ExpectedTag {
+        #[inline]
+        pub fn matcher<T>(self, expected: T) -> EqMatcher<T> {
+            eq(expected)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::prelude::*;
+
+    #[test]
+    fn auto_ref_matcher() -> Result<()> {
+        verify_that!(123, __auto_eq!(ge(9)))
+    }
+
+    #[test]
+    fn auto_ref_expected() -> Result<()> {
+        verify_that!(123, __auto_eq!(123))
+    }
+
+    #[test]
+    fn auto_ref_on_ref_matcher() -> Result<()> {
+        let matcher = eq(123);
+        verify_that!(123, __auto_eq!(&matcher))
+    }
+}
diff --git a/crates/googletest/src/matcher_support/count_elements.rs b/crates/googletest/src/matcher_support/count_elements.rs
index 662dcc9..6d6b8e6 100644
--- a/crates/googletest/src/matcher_support/count_elements.rs
+++ b/crates/googletest/src/matcher_support/count_elements.rs
@@ -18,10 +18,7 @@
 /// unambiguous answer, i.e., the upper bound exists and the lower and upper
 /// bounds agree. Otherwise it iterates through `value` and counts the
 /// elements.
-pub(crate) fn count_elements<ContainerT: ?Sized>(value: &ContainerT) -> usize
-where
-    for<'b> &'b ContainerT: IntoIterator,
-{
+pub(crate) fn count_elements<ContainerT: IntoIterator>(value: ContainerT) -> usize {
     let iterator = value.into_iter();
     if let (lower, Some(higher)) = iterator.size_hint() {
         if lower == higher {
@@ -30,3 +27,53 @@
     }
     iterator.count()
 }
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use crate::prelude::*;
+
+    #[test]
+    fn count_elements_vec() -> Result<()> {
+        verify_that!(count_elements(vec![1, 2, 3]), eq(3))
+    }
+
+    #[test]
+    fn count_elements_with_imprecise_hint() -> Result<()> {
+        struct FakeIterator;
+
+        impl Iterator for FakeIterator {
+            type Item = ();
+
+            fn next(&mut self) -> Option<Self::Item> {
+                None
+            }
+
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                (0, Some(123))
+            }
+        }
+
+        verify_that!(count_elements(FakeIterator), eq(0))
+    }
+
+    #[test]
+    fn count_elements_with_no_hint() -> Result<()> {
+        struct FakeIterator;
+
+        impl Iterator for FakeIterator {
+            type Item = ();
+
+            fn next(&mut self) -> Option<Self::Item> {
+                None
+            }
+
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                (0, None)
+            }
+        }
+
+        verify_that!(count_elements(FakeIterator), eq(0))
+    }
+}
diff --git a/crates/googletest/src/matcher_support/edit_distance.rs b/crates/googletest/src/matcher_support/edit_distance.rs
index 8847bc9..f5e1b9a 100644
--- a/crates/googletest/src/matcher_support/edit_distance.rs
+++ b/crates/googletest/src/matcher_support/edit_distance.rs
@@ -289,7 +289,7 @@
     #[test]
     fn returns_equal_when_strings_are_equal() -> Result<()> {
         let result = edit_list(["A string"], ["A string"], Mode::Exact);
-        verify_that!(result, matches_pattern!(Difference::Equal))
+        verify_that!(result, matches_pattern!(&Difference::Equal))
     }
 
     #[test]
@@ -299,7 +299,7 @@
             ["A string (1)", "A string (2)"],
             Mode::Exact,
         );
-        verify_that!(result, matches_pattern!(Difference::Equal))
+        verify_that!(result, matches_pattern!(&Difference::Equal))
     }
 
     #[test]
@@ -307,8 +307,8 @@
         let result = edit_list(["A string"], [], Mode::Exact);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![matches_pattern!(
-                Edit::ExtraActual(eq("A string"))
+            matches_pattern!(&Difference::Editable(ref elements_are![matches_pattern!(
+                &Edit::ExtraActual(eq("A string"))
             )]))
         )
     }
@@ -318,8 +318,8 @@
         let result = edit_list([], ["A string"], Mode::Exact);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![matches_pattern!(
-                Edit::ExtraExpected(eq("A string"))
+            matches_pattern!(&Difference::Editable(ref elements_are![matches_pattern!(
+                &Edit::ExtraExpected(eq("A string"))
             )]))
         )
     }
@@ -329,9 +329,9 @@
         let result = edit_list(["A string"], ["Another string"], Mode::Exact);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::ExtraActual(eq("A string"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Another string"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::ExtraActual(eq("A string"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Another string"))),
             ]))
         )
     }
@@ -342,11 +342,11 @@
             edit_list(["A string", "A string"], ["Another string", "Another string"], Mode::Exact);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::ExtraActual(eq("A string"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Another string"))),
-                matches_pattern!(Edit::ExtraActual(eq("A string"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Another string"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::ExtraActual(eq("A string"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Another string"))),
+                matches_pattern!(&Edit::ExtraActual(eq("A string"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Another string"))),
             ]))
         )
     }
@@ -360,10 +360,10 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::Both(eq("Common part"))),
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::Both(eq("Common part"))),
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
             ]))
         )
     }
@@ -373,9 +373,9 @@
         let result = edit_list(["Common part", "Actual only"], ["Common part"], Mode::Exact);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::Both(eq("Common part"))),
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::Both(eq("Common part"))),
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
             ]))
         )
     }
@@ -385,9 +385,9 @@
         let result = edit_list(["Common part"], ["Common part", "Expected only"], Mode::Exact);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::Both(eq("Common part"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::Both(eq("Common part"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
             ]))
         )
     }
@@ -401,10 +401,10 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
-                matches_pattern!(Edit::Both(eq("Common part"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+                matches_pattern!(&Edit::Both(eq("Common part"))),
             ]))
         )
     }
@@ -419,19 +419,19 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::ExtraActual(eq("Actual only (1)"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only (1)"))),
-                matches_pattern!(Edit::Both(eq("Common part"))),
-                matches_pattern!(Edit::ExtraActual(eq("Actual only (2)"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only (2)"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only (1)"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only (1)"))),
+                matches_pattern!(&Edit::Both(eq("Common part"))),
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only (2)"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only (2)"))),
             ]))
         )
     }
 
     #[test]
-    fn returns_common_part_plus_difference_plus_common_part_when_there_is_common_prefix_and_suffix()
-    -> Result<()> {
+    fn returns_common_part_plus_difference_plus_common_part_when_there_is_common_prefix_and_suffix(
+    ) -> Result<()> {
         let result = edit_list(
             ["Common part (1)", "Actual only", "Common part (2)"],
             ["Common part (1)", "Expected only", "Common part (2)"],
@@ -439,18 +439,18 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::Both(eq("Common part (1)"))),
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
-                matches_pattern!(Edit::Both(eq("Common part (2)"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::Both(eq("Common part (1)"))),
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+                matches_pattern!(&Edit::Both(eq("Common part (2)"))),
             ]))
         )
     }
 
     #[test]
-    fn returns_common_part_plus_extra_actual_plus_common_part_when_there_is_common_prefix_and_suffix()
-    -> Result<()> {
+    fn returns_common_part_plus_extra_actual_plus_common_part_when_there_is_common_prefix_and_suffix(
+    ) -> Result<()> {
         let result = edit_list(
             ["Common part (1)", "Actual only", "Common part (2)"],
             ["Common part (1)", "Common part (2)"],
@@ -458,17 +458,17 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::Both(eq("Common part (1)"))),
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
-                matches_pattern!(Edit::Both(eq("Common part (2)"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::Both(eq("Common part (1)"))),
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+                matches_pattern!(&Edit::Both(eq("Common part (2)"))),
             ]))
         )
     }
 
     #[test]
-    fn returns_common_part_plus_extra_expected_plus_common_part_when_there_is_common_prefix_and_suffix()
-    -> Result<()> {
+    fn returns_common_part_plus_extra_expected_plus_common_part_when_there_is_common_prefix_and_suffix(
+    ) -> Result<()> {
         let result = edit_list(
             ["Common part (1)", "Common part (2)"],
             ["Common part (1)", "Expected only", "Common part (2)"],
@@ -476,10 +476,10 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::Both(eq("Common part (1)"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
-                matches_pattern!(Edit::Both(eq("Common part (2)"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::Both(eq("Common part (1)"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+                matches_pattern!(&Edit::Both(eq("Common part (2)"))),
             ]))
         )
     }
@@ -493,15 +493,15 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(not(contains(matches_pattern!(
-                Edit::ExtraActual(eq("Actual only"))
+            matches_pattern!(&Difference::Editable(ref not(contains(matches_pattern!(
+                &Edit::ExtraActual(eq("Actual only"))
             )))))
         )
     }
 
     #[test]
-    fn does_not_skip_extra_parts_on_actual_in_prefix_mode_at_end_when_they_are_in_common()
-    -> Result<()> {
+    fn does_not_skip_extra_parts_on_actual_in_prefix_mode_at_end_when_they_are_in_common(
+    ) -> Result<()> {
         let result = edit_list(
             ["Actual only", "Common part"],
             ["Expected only", "Common part"],
@@ -509,24 +509,24 @@
         );
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
-                matches_pattern!(Edit::Both(eq("Common part"))),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+                matches_pattern!(&Edit::Both(eq("Common part"))),
             ]))
         )
     }
 
     #[test]
-    fn does_not_skip_corresponding_line_on_actual_when_actual_and_expected_differ_in_prefix_mode()
-    -> Result<()> {
+    fn does_not_skip_corresponding_line_on_actual_when_actual_and_expected_differ_in_prefix_mode(
+    ) -> Result<()> {
         let result = edit_list(["Actual only"], ["Expected only"], Mode::Prefix);
         verify_that!(
             result,
-            matches_pattern!(Difference::Editable(elements_are![
-                matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
-                matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
-                matches_pattern!(Edit::AdditionalActual),
+            matches_pattern!(&Difference::Editable(ref elements_are![
+                matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+                matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+                matches_pattern!(&Edit::AdditionalActual),
             ]))
         )
     }
@@ -534,7 +534,7 @@
     #[test]
     fn returns_unrelated_when_maximum_distance_exceeded() -> Result<()> {
         let result = edit_list(0..=50, 60..110, Mode::Exact);
-        verify_that!(result, matches_pattern!(Difference::Unrelated))
+        verify_that!(result, matches_pattern!(&Difference::Unrelated))
     }
 
     quickcheck! {
diff --git a/crates/googletest/src/matcher_support/mod.rs b/crates/googletest/src/matcher_support/mod.rs
index 8a72ba4..8e138a1 100644
--- a/crates/googletest/src/matcher_support/mod.rs
+++ b/crates/googletest/src/matcher_support/mod.rs
@@ -18,7 +18,13 @@
 //! these facilities could be useful to downstream users writing custom
 //! matchers.
 
+mod auto_eq;
 pub(crate) mod count_elements;
 pub(crate) mod edit_distance;
 pub(crate) mod summarize_diff;
 pub(crate) mod zipped_iterator;
+
+pub mod __internal_unstable_do_not_depend_on_these {
+    pub use super::auto_eq::internal::{ExpectedKind, Wrapper};
+    pub use crate::__auto_eq as auto_eq;
+}
diff --git a/crates/googletest/src/matcher_support/summarize_diff.rs b/crates/googletest/src/matcher_support/summarize_diff.rs
index cd4efa7..b7cf641 100644
--- a/crates/googletest/src/matcher_support/summarize_diff.rs
+++ b/crates/googletest/src/matcher_support/summarize_diff.rs
@@ -17,7 +17,7 @@
 use crate::matcher_support::edit_distance;
 #[rustversion::since(1.70)]
 use std::io::IsTerminal;
-use std::{borrow::Cow, fmt::Display};
+use std::{borrow::Cow, cell::Cell, fmt::Display};
 
 /// Returns a string describing how the expected and actual lines differ.
 ///
@@ -38,10 +38,29 @@
         // line-by-line diff.
         return "".into();
     }
+
     match edit_distance::edit_list(actual_debug.lines(), expected_debug.lines(), diff_mode) {
-        edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
+        edit_distance::Difference::Equal => {
+            // str.lines() is oblivious to the last newline in a
+            // string, so we need to check this to make sure we don't spuriously
+            // claim that 'hello' and 'hello\n' are identical debug strings.
+            //
+            // Although we would have liked to resolve by replacing
+            // str::lines() with str::split('\n'), the potentially
+            // empty last element interferes with good diff output for
+            // "contains" checks.
+            let actual_newline_terminated = actual_debug.ends_with('\n');
+            let expected_newline_terminated = expected_debug.ends_with('\n');
+            if actual_newline_terminated && !expected_newline_terminated {
+                "Actual includes a terminating newline that is absent from expected.".into()
+            } else if !actual_newline_terminated && expected_newline_terminated {
+                "Actual omits a terminating newline that is present in expected.".into()
+            } else {
+                "No difference found between debug strings.".into()
+            }
+        }
         edit_distance::Difference::Editable(edit_list) => {
-            format!("\n{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
+            format!("{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
                 .into()
         }
         edit_distance::Difference::Unrelated => "".into(),
@@ -73,7 +92,7 @@
         edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
         edit_distance::Difference::Editable(mut edit_list) => {
             edit_list.reverse();
-            format!("\n{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
+            format!("{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
                 .into()
         }
         edit_distance::Difference::Unrelated => "".into(),
@@ -81,9 +100,9 @@
 }
 
 // Produces the header, with or without coloring depending on
-// stdout_supports_color()
+// USE_COLOR
 fn summary_header() -> Cow<'static, str> {
-    if stdout_supports_color() {
+    if USE_COLOR.with(Cell::get) {
         format!(
             "Difference(-{ACTUAL_ONLY_STYLE}actual{RESET_ALL} / +{EXPECTED_ONLY_STYLE}expected{RESET_ALL}):"
         ).into()
@@ -105,11 +124,11 @@
 impl<'a> BufferedSummary<'a> {
     // Appends a new line which is common to both actual and expected.
     fn feed_common_lines(&mut self, common_line: &'a str) {
-        if let Buffer::CommonLineBuffer(ref mut common_lines) = self.buffer {
+        if let Buffer::CommonLines(ref mut common_lines) = self.buffer {
             common_lines.push(common_line);
         } else {
             self.flush_buffer();
-            self.buffer = Buffer::CommonLineBuffer(vec![common_line]);
+            self.buffer = Buffer::CommonLines(vec![common_line]);
         }
     }
 
@@ -229,7 +248,7 @@
 
 enum Buffer<'a> {
     Empty,
-    CommonLineBuffer(Vec<&'a str>),
+    CommonLines(Vec<&'a str>),
     ExtraActualLineChunk(&'a str),
     ExtraExpectedLineChunk(&'a str),
 }
@@ -238,7 +257,7 @@
     fn flush(&mut self, summary: &mut SummaryBuilder) {
         match self {
             Buffer::Empty => {}
-            Buffer::CommonLineBuffer(common_lines) => {
+            Buffer::CommonLines(common_lines) => {
                 Self::flush_common_lines(std::mem::take(common_lines), summary);
             }
             Buffer::ExtraActualLineChunk(extra_actual) => {
@@ -294,8 +313,13 @@
     }
 }
 
+thread_local! {
+  pub(crate) static USE_COLOR: Cell<bool> = Cell::new(stdout_supports_color());
+}
+
 #[rustversion::since(1.70)]
 fn stdout_supports_color() -> bool {
+    #[allow(clippy::incompatible_msrv)]
     match (is_env_var_set("NO_COLOR"), is_env_var_set("FORCE_COLOR")) {
         (true, _) => false,
         (false, true) => true,
@@ -305,7 +329,7 @@
 
 #[rustversion::not(since(1.70))]
 fn stdout_supports_color() -> bool {
-    is_env_var_set("FORCE_COLOR")
+    is_env_var_set("FORCE_COLOR") && !is_env_var_set("NO_COLOR")
 }
 
 fn is_env_var_set(var: &'static str) -> bool {
@@ -388,14 +412,14 @@
     }
 
     fn reset_ansi(&mut self) {
-        if !self.last_ansi_style.is_empty() && stdout_supports_color() {
+        if !self.last_ansi_style.is_empty() && USE_COLOR.with(Cell::get) {
             self.summary.push_str(RESET_ALL);
             self.last_ansi_style = "";
         }
     }
 
     fn set_ansi(&mut self, ansi_style: &'static str) {
-        if !stdout_supports_color() || self.last_ansi_style == ansi_style {
+        if !USE_COLOR.with(Cell::get) || self.last_ansi_style == ansi_style {
             return;
         }
         if !self.last_ansi_style.is_empty() {
@@ -411,7 +435,6 @@
     use super::*;
     use crate::{matcher_support::edit_distance::Mode, prelude::*};
     use indoc::indoc;
-    use serial_test::{parallel, serial};
     use std::fmt::Write;
 
     // Make a long text with each element of the iterator on one line.
@@ -427,13 +450,11 @@
     }
 
     #[test]
-    #[parallel]
     fn create_diff_smaller_than_one_line() -> Result<()> {
         verify_that!(create_diff("One", "Two", Mode::Exact), eq(""))
     }
 
     #[test]
-    #[parallel]
     fn create_diff_exact_same() -> Result<()> {
         let expected = indoc! {"
             One
@@ -450,7 +471,6 @@
     }
 
     #[test]
-    #[parallel]
     fn create_diff_multiline_diff() -> Result<()> {
         let expected = indoc! {"
             prefix
@@ -469,7 +489,6 @@
             create_diff(expected, actual, Mode::Exact),
             eq(indoc!(
                 "
-
                 Difference(-actual / +expected):
                  prefix
                 -Actual#1
@@ -483,19 +502,16 @@
     }
 
     #[test]
-    #[parallel]
     fn create_diff_exact_unrelated() -> Result<()> {
         verify_that!(create_diff(&build_text(1..500), &build_text(501..1000), Mode::Exact), eq(""))
     }
 
     #[test]
-    #[parallel]
     fn create_diff_exact_small_difference() -> Result<()> {
         verify_that!(
             create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
             eq(indoc! {
                 "
-
                 Difference(-actual / +expected):
                  1
                  2
@@ -507,33 +523,14 @@
         )
     }
 
-    // Test with color enabled.
-
-    struct ForceColor;
-
-    fn force_color() -> ForceColor {
-        std::env::set_var("FORCE_COLOR", "1");
-        std::env::remove_var("NO_COLOR");
-        ForceColor
-    }
-
-    impl Drop for ForceColor {
-        fn drop(&mut self) {
-            std::env::remove_var("FORCE_COLOR");
-            std::env::set_var("NO_COLOR", "1");
-        }
-    }
-
     #[test]
-    #[serial]
     fn create_diff_exact_small_difference_with_color() -> Result<()> {
-        let _keep = force_color();
+        USE_COLOR.with(|cell| cell.set(true));
 
         verify_that!(
             create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
             eq(indoc! {
                 "
-
                 Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
                  1
                  2
@@ -546,9 +543,9 @@
     }
 
     #[test]
-    #[serial]
     fn create_diff_exact_difference_with_inline_color() -> Result<()> {
-        let _keep = force_color();
+        USE_COLOR.with(|cell| cell.set(true));
+
         let actual = indoc!(
             "There is a home in Nouvelle Orleans
             They say, it is the rising sons
@@ -565,7 +562,6 @@
             create_diff(actual, expected, Mode::Exact),
             eq(indoc! {
                 "
-
                 Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
                 -\x1B[31mThere is a ho\x1B[0m\x1B[1;31mm\x1B[0m\x1B[31me in N\x1B[0m\x1B[1;31mouv\x1B[0m\x1B[31me\x1B[0m\x1B[1;31mlle\x1B[0m\x1B[31m Orleans\x1B[0m
                 +\x1B[32mThere is a ho\x1B[0m\x1B[1;32mus\x1B[0m\x1B[32me \x1B[0m\x1B[1;32mway down \x1B[0m\x1B[32min Ne\x1B[0m\x1B[1;32mw\x1B[0m\x1B[32m Orleans\x1B[0m
@@ -576,4 +572,20 @@
             })
         )
     }
+
+    #[test]
+    fn create_diff_line_termination_diff() -> Result<()> {
+        verify_that!(
+            create_diff("1\n2\n3", "1\n2\n3\n", Mode::Exact),
+            eq("Actual omits a terminating newline that is present in expected.")
+        )?;
+        verify_that!(
+            create_diff("1\n2\n3\n", "1\n2\n3", Mode::Exact),
+            eq("Actual includes a terminating newline that is absent from expected.")
+        )?;
+        verify_that!(
+            create_diff("1\n2\n3\n", "1\n2\n3\n", Mode::Exact),
+            eq("No difference found between debug strings.")
+        )
+    }
 }
diff --git a/crates/googletest/src/matchers/all_matcher.rs b/crates/googletest/src/matchers/all_matcher.rs
index f2e6d06..d03e7d4 100644
--- a/crates/googletest/src/matchers/all_matcher.rs
+++ b/crates/googletest/src/matchers/all_matcher.rs
@@ -38,7 +38,7 @@
 /// ```
 ///
 /// Using this macro is equivalent to using the
-/// [`and`][crate::matcher::Matcher::and] method:
+/// [`and`][crate::matcher::MatcherBase::and] method:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -50,109 +50,41 @@
 /// ```
 ///
 /// Assertion failure messages are not guaranteed to be identical, however.
+///
+///  If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(123, all![123, lt(1000), gt(100)])
+/// #     .unwrap();
+/// ```
 #[macro_export]
 #[doc(hidden)]
 macro_rules! __all {
-    ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::AllMatcher;
-        AllMatcher::new([$(Box::new($matcher)),*])
+    ($(,)?) => {{
+        $crate::matchers::anything()
+    }} ;
+    ($matcher:expr $(,)?) => {{
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+        auto_eq!($matcher)
+    }};
+    ($head:expr, $head2:expr $(,)?) => {{
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher::new(auto_eq!($head), auto_eq!($head2))
+    }};
+    ($head:expr, $head2:expr, $($tail:expr),+ $(,)?) => {{
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+        $crate::__all![
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher::new(auto_eq!($head), auto_eq!($head2)),
+            $($tail),+
+        ]
     }}
 }
 
-/// Functionality needed by the [`all`] macro.
-///
-/// For internal use only. API stablility is not guaranteed!
-#[doc(hidden)]
-pub mod internal {
-    use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
-    use crate::matchers::anything;
-    use std::fmt::Debug;
-
-    /// A matcher which matches an input value matched by all matchers in the
-    /// array `components`.
-    ///
-    /// For internal use only. API stablility is not guaranteed!
-    #[doc(hidden)]
-    pub struct AllMatcher<'a, T: Debug + ?Sized, const N: usize> {
-        components: [Box<dyn Matcher<ActualT = T> + 'a>; N],
-    }
-
-    impl<'a, T: Debug + ?Sized, const N: usize> AllMatcher<'a, T, N> {
-        /// Constructs an [`AllMatcher`] with the given component matchers.
-        ///
-        /// Intended for use only by the [`all`] macro.
-        pub fn new(components: [Box<dyn Matcher<ActualT = T> + 'a>; N]) -> Self {
-            Self { components }
-        }
-    }
-
-    impl<'a, T: Debug + ?Sized, const N: usize> Matcher for AllMatcher<'a, T, N> {
-        type ActualT = T;
-
-        fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-            for component in &self.components {
-                match component.matches(actual) {
-                    MatcherResult::NoMatch => {
-                        return MatcherResult::NoMatch;
-                    }
-                    MatcherResult::Match => {}
-                }
-            }
-            MatcherResult::Match
-        }
-
-        fn explain_match(&self, actual: &Self::ActualT) -> Description {
-            match N {
-                0 => anything::<T>().explain_match(actual),
-                1 => self.components[0].explain_match(actual),
-                _ => {
-                    let failures = self
-                        .components
-                        .iter()
-                        .filter(|component| component.matches(actual).is_no_match())
-                        .collect::<Vec<_>>();
-
-                    if failures.len() == 1 {
-                        failures[0].explain_match(actual)
-                    } else {
-                        Description::new()
-                            .collect(
-                                failures
-                                    .into_iter()
-                                    .map(|component| component.explain_match(actual)),
-                            )
-                            .bullet_list()
-                    }
-                }
-            }
-        }
-
-        fn describe(&self, matcher_result: MatcherResult) -> Description {
-            match N {
-                0 => anything::<T>().describe(matcher_result),
-                1 => self.components[0].describe(matcher_result),
-                _ => {
-                    let header = if matcher_result.into() {
-                        "has all the following properties:"
-                    } else {
-                        "has at least one of the following properties:"
-                    };
-                    Description::new().text(header).nested(
-                        Description::new()
-                            .bullet_list()
-                            .collect(self.components.iter().map(|m| m.describe(matcher_result))),
-                    )
-                }
-            }
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
-    use super::internal;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -160,10 +92,10 @@
     fn description_shows_more_than_one_matcher() -> Result<()> {
         let first_matcher = starts_with("A");
         let second_matcher = ends_with("string");
-        let matcher: internal::AllMatcher<String, 2> = all!(first_matcher, second_matcher);
+        let matcher = all!(first_matcher, second_matcher);
 
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<&String>::describe(&matcher, MatcherResult::Match),
             displays_as(eq(indoc!(
                 "
                 has all the following properties:
@@ -176,10 +108,10 @@
     #[test]
     fn description_shows_one_matcher_directly() -> Result<()> {
         let first_matcher = starts_with("A");
-        let matcher: internal::AllMatcher<String, 1> = all!(first_matcher);
+        let matcher = all!(first_matcher);
 
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<&String>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("starts with prefix \"A\""))
         )
     }
@@ -189,7 +121,7 @@
     {
         let first_matcher = starts_with("Another");
         let second_matcher = ends_with("string");
-        let matcher: internal::AllMatcher<str, 2> = all!(first_matcher, second_matcher);
+        let matcher = all!(first_matcher, second_matcher);
 
         verify_that!(
             matcher.explain_match("A string"),
@@ -200,11 +132,16 @@
     #[test]
     fn mismatch_description_is_simple_when_only_one_consistuent() -> Result<()> {
         let first_matcher = starts_with("Another");
-        let matcher: internal::AllMatcher<str, 1> = all!(first_matcher);
+        let matcher = all!(first_matcher);
 
         verify_that!(
             matcher.explain_match("A string"),
             displays_as(eq("which does not start with \"Another\""))
         )
     }
+
+    #[test]
+    fn all_with_auto_eq() -> Result<()> {
+        verify_that!(42, all![eq(42), 42, lt(100)])
+    }
 }
diff --git a/crates/googletest/src/matchers/any_matcher.rs b/crates/googletest/src/matchers/any_matcher.rs
index 95d53fe..6f988c8 100644
--- a/crates/googletest/src/matchers/any_matcher.rs
+++ b/crates/googletest/src/matchers/any_matcher.rs
@@ -40,7 +40,7 @@
 /// ```
 ///
 /// Using this macro is equivalent to using the
-/// [`or`][crate::matcher::Matcher::or] method:
+/// [`or`][crate::matcher::MatcherBase::or] method:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -52,107 +52,41 @@
 /// ```
 ///
 /// Assertion failure messages are not guaranteed to be identical, however.
+///
+///  If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(123, any![lt(1), 123, gt(1000)])
+/// #     .unwrap();
+/// ```
 #[macro_export]
 #[doc(hidden)]
 macro_rules! __any {
-    ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::AnyMatcher;
-        AnyMatcher::new([$(Box::new($matcher)),*])
+    ($(,)?) => {{
+        $crate::matchers::not($crate::matchers::anything())
+    }} ;
+    ($matcher:expr $(,)?) => {{
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+        auto_eq!($matcher)
+    }};
+    ($head:expr, $head2:expr $(,)?) => {{
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher::new(auto_eq!($head), auto_eq!($head2))
+    }};
+    ($head:expr, $head2:expr, $($tail:expr),+ $(,)?) => {{
+        use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+        $crate::__any![
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher::new(auto_eq!($head), auto_eq!($head2)),
+            $($tail),+
+        ]
     }}
 }
 
-/// Functionality needed by the [`any`] macro.
-///
-/// For internal use only. API stablility is not guaranteed!
-#[doc(hidden)]
-pub mod internal {
-    use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
-    use crate::matchers::anything;
-    use std::fmt::Debug;
-
-    /// A matcher which matches an input value matched by all matchers in the
-    /// array `components`.
-    ///
-    /// For internal use only. API stablility is not guaranteed!
-    #[doc(hidden)]
-    pub struct AnyMatcher<'a, T: Debug + ?Sized, const N: usize> {
-        components: [Box<dyn Matcher<ActualT = T> + 'a>; N],
-    }
-
-    impl<'a, T: Debug + ?Sized, const N: usize> AnyMatcher<'a, T, N> {
-        /// Constructs an [`AnyMatcher`] with the given component matchers.
-        ///
-        /// Intended for use only by the [`all`] macro.
-        pub fn new(components: [Box<dyn Matcher<ActualT = T> + 'a>; N]) -> Self {
-            Self { components }
-        }
-    }
-
-    impl<'a, T: Debug + ?Sized, const N: usize> Matcher for AnyMatcher<'a, T, N> {
-        type ActualT = T;
-
-        fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-            MatcherResult::from(self.components.iter().any(|c| c.matches(actual).is_match()))
-        }
-
-        fn explain_match(&self, actual: &Self::ActualT) -> Description {
-            match N {
-                0 => format!("which {}", anything::<T>().describe(MatcherResult::NoMatch)).into(),
-                1 => self.components[0].explain_match(actual),
-                _ => {
-                    let failures = self
-                        .components
-                        .iter()
-                        .filter(|component| component.matches(actual).is_no_match())
-                        .collect::<Vec<_>>();
-
-                    if failures.len() == 1 {
-                        failures[0].explain_match(actual)
-                    } else {
-                        Description::new()
-                            .collect(
-                                failures
-                                    .into_iter()
-                                    .map(|component| component.explain_match(actual)),
-                            )
-                            .bullet_list()
-                    }
-                }
-            }
-        }
-
-        fn describe(&self, matcher_result: MatcherResult) -> Description {
-            match N {
-                0 => anything::<T>().describe(matcher_result),
-                1 => self.components[0].describe(matcher_result),
-                _ => {
-                    let properties = self
-                        .components
-                        .iter()
-                        .map(|m| m.describe(matcher_result))
-                        .collect::<Description>()
-                        .bullet_list()
-                        .indent();
-                    format!(
-                        "{}:\n{properties}",
-                        if matcher_result.into() {
-                            "has at least one of the following properties"
-                        } else {
-                            "has none of the following properties"
-                        }
-                    )
-                    .into()
-                }
-            }
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
-    use super::internal;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -160,10 +94,10 @@
     fn description_shows_more_than_one_matcher() -> Result<()> {
         let first_matcher = starts_with("A");
         let second_matcher = ends_with("string");
-        let matcher: internal::AnyMatcher<String, 2> = any!(first_matcher, second_matcher);
+        let matcher = any!(first_matcher, second_matcher);
 
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<&String>::describe(&matcher, MatcherResult::Match),
             displays_as(eq(indoc!(
                 "
                 has at least one of the following properties:
@@ -176,10 +110,10 @@
     #[test]
     fn description_shows_one_matcher_directly() -> Result<()> {
         let first_matcher = starts_with("A");
-        let matcher: internal::AnyMatcher<String, 1> = any!(first_matcher);
+        let matcher = any!(first_matcher);
 
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("starts with prefix \"A\""))
         )
     }
@@ -189,7 +123,7 @@
     {
         let first_matcher = starts_with("Another");
         let second_matcher = ends_with("string");
-        let matcher: internal::AnyMatcher<str, 2> = any!(first_matcher, second_matcher);
+        let matcher = any!(first_matcher, second_matcher);
 
         verify_that!(
             matcher.explain_match("A string"),
@@ -200,11 +134,21 @@
     #[test]
     fn mismatch_description_is_simple_when_only_one_constituent() -> Result<()> {
         let first_matcher = starts_with("Another");
-        let matcher: internal::AnyMatcher<str, 1> = any!(first_matcher);
+        let matcher = any!(first_matcher);
 
         verify_that!(
             matcher.explain_match("A string"),
             displays_as(eq("which does not start with \"Another\""))
         )
     }
+
+    #[test]
+    fn empty_any_matcher_never_matches() -> Result<()> {
+        verify_that!(123, not(any![]))
+    }
+
+    #[test]
+    fn any_with_auto_eq() -> Result<()> {
+        verify_that!(42, any![1, 2, 42, gt(123)])
+    }
 }
diff --git a/crates/googletest/src/matchers/anything_matcher.rs b/crates/googletest/src/matchers/anything_matcher.rs
index 36be478..a920974 100644
--- a/crates/googletest/src/matchers/anything_matcher.rs
+++ b/crates/googletest/src/matchers/anything_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches anything. This matcher always succeeds.
 ///
@@ -32,16 +32,15 @@
 /// # }
 /// # should_pass().unwrap();
 /// ```
-pub fn anything<T: Debug + ?Sized>() -> impl Matcher<ActualT = T> {
-    Anything::<T>(Default::default())
+pub fn anything() -> Anything {
+    Anything
 }
 
-struct Anything<T: ?Sized>(PhantomData<T>);
+#[derive(MatcherBase)]
+pub struct Anything;
 
-impl<T: Debug + ?Sized> Matcher for Anything<T> {
-    type ActualT = T;
-
-    fn matches(&self, _: &T) -> MatcherResult {
+impl<T: Debug + Copy> Matcher<T> for Anything {
+    fn matches(&self, _: T) -> MatcherResult {
         MatcherResult::Match
     }
 
@@ -55,7 +54,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::anything;
     use crate::prelude::*;
 
     #[test]
diff --git a/crates/googletest/src/matchers/bool_matcher.rs b/crates/googletest/src/matchers/bool_matcher.rs
new file mode 100644
index 0000000..380e011
--- /dev/null
+++ b/crates/googletest/src/matchers/bool_matcher.rs
@@ -0,0 +1,99 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherBase, MatcherResult},
+};
+
+/// Matches boolean value `true`.
+pub fn is_true() -> BoolMatcher {
+    BoolMatcher { expected: true }
+}
+
+/// Matches boolean value `false`.
+pub fn is_false() -> BoolMatcher {
+    BoolMatcher { expected: false }
+}
+
+/// Matches a bool value or bool reference.
+#[derive(MatcherBase)]
+pub struct BoolMatcher {
+    expected: bool,
+}
+
+impl BoolMatcher {
+    fn matches(&self, actual: bool) -> MatcherResult {
+        (actual == self.expected).into()
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        match (matcher_result, self.expected) {
+            (MatcherResult::Match, true) | (MatcherResult::NoMatch, false) => "is true".into(),
+            (MatcherResult::Match, false) | (MatcherResult::NoMatch, true) => "is false".into(),
+        }
+    }
+}
+
+impl Matcher<bool> for BoolMatcher {
+    fn matches(&self, actual: bool) -> MatcherResult {
+        self.matches(actual)
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        self.describe(matcher_result)
+    }
+}
+
+impl<'a> Matcher<&'a bool> for BoolMatcher {
+    fn matches(&self, actual: &'a bool) -> MatcherResult {
+        self.matches(*actual)
+    }
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        self.describe(matcher_result)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::prelude::*;
+
+    #[test]
+    fn match_value() -> Result<()> {
+        verify_that!(true, is_true())?;
+        verify_that!(true, not(is_false()))?;
+        verify_that!(false, is_false())?;
+        verify_that!(false, not(is_true()))
+    }
+
+    #[test]
+    fn match_ref() -> Result<()> {
+        let t = true;
+        let f = false;
+
+        verify_that!(&t, is_true())?;
+        verify_that!(&t, not(is_false()))?;
+        verify_that!(&f, is_false())?;
+        verify_that!(&f, not(is_true()))
+    }
+
+    #[test]
+    fn describe() {
+        assert_eq!(is_true().describe(MatcherResult::Match).to_string(), "is true");
+        assert_eq!(is_true().describe(MatcherResult::NoMatch).to_string(), "is false");
+        assert_eq!(is_false().describe(MatcherResult::Match).to_string(), "is false");
+        assert_eq!(is_false().describe(MatcherResult::NoMatch).to_string(), "is true");
+    }
+}
diff --git a/crates/googletest/src/matchers/char_count_matcher.rs b/crates/googletest/src/matchers/char_count_matcher.rs
index 70977d7..4f11edd 100644
--- a/crates/googletest/src/matchers/char_count_matcher.rs
+++ b/crates/googletest/src/matchers/char_count_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a string whose number of Unicode scalars matches `expected`.
 ///
@@ -56,22 +56,18 @@
 /// # }
 /// # should_pass().unwrap();
 /// ```
-pub fn char_count<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>>(
-    expected: E,
-) -> impl Matcher<ActualT = T> {
-    CharLenMatcher { expected, phantom: Default::default() }
+pub fn char_count<E: Matcher<usize>>(expected: E) -> CharLenMatcher<E> {
+    CharLenMatcher { expected }
 }
 
-struct CharLenMatcher<T: ?Sized, E> {
+#[derive(MatcherBase)]
+pub struct CharLenMatcher<E> {
     expected: E,
-    phantom: PhantomData<T>,
 }
 
-impl<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>> Matcher for CharLenMatcher<T, E> {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
-        self.expected.matches(&actual.as_ref().chars().count())
+impl<T: Debug + Copy + AsRef<str>, E: Matcher<usize>> Matcher<T> for CharLenMatcher<E> {
+    fn matches(&self, actual: T) -> MatcherResult {
+        self.expected.matches(actual.as_ref().chars().count())
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -89,12 +85,12 @@
         }
     }
 
-    fn explain_match(&self, actual: &T) -> Description {
+    fn explain_match(&self, actual: T) -> Description {
         let actual_size = actual.as_ref().chars().count();
         format!(
             "which has character count {}, {}",
             actual_size,
-            self.expected.explain_match(&actual_size)
+            self.expected.explain_match(actual_size)
         )
         .into()
     }
@@ -102,13 +98,11 @@
 
 #[cfg(test)]
 mod tests {
-    use super::char_count;
     use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::fmt::Debug;
-    use std::marker::PhantomData;
 
     #[test]
     fn char_count_matches_string_slice() -> Result<()> {
@@ -130,11 +124,11 @@
 
     #[test]
     fn char_count_explains_match() -> Result<()> {
-        struct TestMatcher<T>(PhantomData<T>);
-        impl<T: Debug> Matcher for TestMatcher<T> {
-            type ActualT = T;
+        #[derive(MatcherBase)]
+        struct TestMatcher;
 
-            fn matches(&self, _: &T) -> MatcherResult {
+        impl<T: Debug + Copy> Matcher<T> for TestMatcher {
+            fn matches(&self, _: T) -> MatcherResult {
                 false.into()
             }
 
@@ -142,12 +136,12 @@
                 "called described".into()
             }
 
-            fn explain_match(&self, _: &T) -> Description {
+            fn explain_match(&self, _: T) -> Description {
                 "called explain_match".into()
             }
         }
         verify_that!(
-            char_count(TestMatcher(Default::default())).explain_match(&"A string"),
+            char_count(TestMatcher).explain_match("A string"),
             displays_as(eq("which has character count 8, called explain_match"))
         )
     }
diff --git a/crates/googletest/src/matchers/conjunction_matcher.rs b/crates/googletest/src/matchers/conjunction_matcher.rs
index dc50833..b2e2e27 100644
--- a/crates/googletest/src/matchers/conjunction_matcher.rs
+++ b/crates/googletest/src/matchers/conjunction_matcher.rs
@@ -17,56 +17,87 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
 use std::fmt::Debug;
 
-/// Matcher created by [`Matcher::and`].
+/// Matcher created by [`Matcher::and`] and [`all!`].
+///
+/// Both [`Matcher::and`] and [`all!`] nest on m1. In other words,
+/// both `x.and(y).and(z)` and `all![x, y, z]` produce:
+/// ```ignore
+/// ConjunctionMatcher {
+///     m1: ConjunctionMatcher {
+///         m1: x,
+///         m2: y
+///     },
+///     m2: z
+/// }
+/// ```
+///
+/// This behavior must be respected
+/// to ensure that [`Matcher::explain_match`] and [`Matcher::describe`] produce
+/// useful descriptions.
 ///
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
+#[derive(MatcherBase)]
 pub struct ConjunctionMatcher<M1, M2> {
     m1: M1,
     m2: M2,
 }
 
 impl<M1, M2> ConjunctionMatcher<M1, M2> {
-    pub(crate) fn new(m1: M1, m2: M2) -> Self {
+    pub fn new(m1: M1, m2: M2) -> Self {
         Self { m1, m2 }
     }
 }
 
-impl<M1: Matcher, M2: Matcher<ActualT = M1::ActualT>> Matcher for ConjunctionMatcher<M1, M2>
-where
-    M1::ActualT: Debug,
-{
-    type ActualT = M1::ActualT;
-
-    fn matches(&self, actual: &M1::ActualT) -> MatcherResult {
+impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for ConjunctionMatcher<M1, M2> {
+    fn matches(&self, actual: T) -> MatcherResult {
         match (self.m1.matches(actual), self.m2.matches(actual)) {
             (MatcherResult::Match, MatcherResult::Match) => MatcherResult::Match,
             _ => MatcherResult::NoMatch,
         }
     }
 
-    fn explain_match(&self, actual: &M1::ActualT) -> Description {
+    fn explain_match(&self, actual: T) -> Description {
         match (self.m1.matches(actual), self.m2.matches(actual)) {
-            (MatcherResult::Match, MatcherResult::Match) => Description::new()
-                .nested(self.m1.explain_match(actual))
-                .text("and")
-                .nested(self.m2.explain_match(actual)),
             (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
             (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
-            (MatcherResult::NoMatch, MatcherResult::NoMatch) => Description::new()
-                .nested(self.m1.explain_match(actual))
-                .text("and")
-                .nested(self.m2.explain_match(actual)),
+            (_, _) => {
+                let m1_description = self.m1.explain_match(actual);
+                if m1_description.is_conjunction_description() {
+                    m1_description.nested(self.m2.explain_match(actual))
+                } else {
+                    Description::new()
+                        .bullet_list()
+                        .collect([m1_description, self.m2.explain_match(actual)])
+                        .conjunction_description()
+                }
+            }
         }
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
-        format!("{}, and {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result))
-            .into()
+        let m1_description = self.m1.describe(matcher_result);
+        if m1_description.is_conjunction_description() {
+            m1_description.push_in_last_nested(self.m2.describe(matcher_result))
+        } else {
+            let header = if matcher_result.into() {
+                "has all the following properties:"
+            } else {
+                "has at least one of the following properties:"
+            };
+            Description::new()
+                .text(header)
+                .nested(
+                    Description::new()
+                        .bullet_list()
+                        .collect([m1_description, self.m2.describe(matcher_result)]),
+                )
+                .conjunction_description()
+        }
     }
 }
 
@@ -88,7 +119,9 @@
             err(displays_as(contains_substring(indoc!(
                 "
                 Value of: 1
-                Expected: is anything, and never matches
+                Expected: has all the following properties:
+                  * is anything
+                  * never matches
                 Actual: 1,
                   which is anything
                 "
@@ -104,7 +137,9 @@
             err(displays_as(contains_substring(indoc!(
                 "
                     Value of: 1
-                    Expected: never matches, and is anything
+                    Expected: has all the following properties:
+                      * never matches
+                      * is anything
                     Actual: 1,
                       which is anything
                 "
@@ -120,11 +155,37 @@
             err(displays_as(contains_substring(indoc!(
                 "
                 Value of: 1
-                Expected: never matches, and never matches
+                Expected: has all the following properties:
+                  * never matches
+                  * never matches
                 Actual: 1,
-                    which is anything
-                  and
-                    which is anything
+                  * which is anything
+                  * which is anything
+                "
+            ))))
+        )
+    }
+
+    #[test]
+    fn and_long_chain_of_matchers() -> Result<()> {
+        let result = verify_that!(
+            1,
+            anything().and(not(anything())).and(anything()).and(not(anything())).and(anything())
+        );
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                "
+                Value of: 1
+                Expected: has all the following properties:
+                  * is anything
+                  * never matches
+                  * is anything
+                  * never matches
+                  * is anything
+                Actual: 1,
+                  * which is anything
+                  * which is anything
                 "
             ))))
         )
@@ -132,7 +193,7 @@
 
     #[test]
     fn chained_and_matches() -> Result<()> {
-        #[derive(Debug)]
+        #[derive(Debug, Clone, Copy)]
         struct Struct {
             a: i32,
             b: i32,
diff --git a/crates/googletest/src/matchers/container_eq_matcher.rs b/crates/googletest/src/matchers/container_eq_matcher.rs
index d4f872c..7eb0933 100644
--- a/crates/googletest/src/matchers/container_eq_matcher.rs
+++ b/crates/googletest/src/matchers/container_eq_matcher.rs
@@ -13,9 +13,8 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use std::fmt::Debug;
-use std::marker::PhantomData;
 
 /// Matches a container equal (in the sense of `==`) to `expected`.
 ///
@@ -30,15 +29,12 @@
 ///   Unexpected: [4]
 /// ```
 ///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// dereferenced slice. More precisely, the actual value must
 /// implement [`IntoIterator`] whose `Item` type implements
 /// [`PartialEq<ExpectedT>`], where `ExpectedT` is the element type of the
 /// expected value.
 ///
-/// If the container type is a `Vec`, then the expected type may be a slice of
-/// the same element type. For example:
-///
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
@@ -49,78 +45,40 @@
 /// # should_pass().unwrap();
 /// ```
 ///
-/// As an exception, if the actual type is a `Vec<String>`, the expected type
-/// may be a slice of `&str`:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let vec: Vec<String> = vec!["A string".into(), "Another string".into()];
-/// verify_that!(vec, container_eq(["A string", "Another string"]))?;
-/// #     Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// These exceptions allow one to avoid unnecessary allocations in test
-/// assertions.
-///
-/// One can also check container equality of a slice with an array. To do so,
-/// dereference the slice:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, container_eq([1, 2, 3]))?;
-/// #     Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// Otherwise, the actual and expected types must be identical.
-///
 /// *Performance note*: In the event of a mismatch leading to an assertion
 /// failure, the construction of the lists of missing and unexpected values
 /// uses a naive algorithm requiring time proportional to the product of the
 /// sizes of the expected and actual values. This should therefore only be used
 /// when the containers are small enough that this is not a problem.
-// This returns ContainerEqMatcher and not impl Matcher because
-// ContainerEqMatcher has some specialisations for slice types (see
-// documentation above). Returning impl Matcher would hide those from the
-// compiler.
-pub fn container_eq<ActualContainerT, ExpectedContainerT>(
+pub fn container_eq<ExpectedContainerT>(
     expected: ExpectedContainerT,
-) -> ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
+) -> ContainerEqMatcher<ExpectedContainerT>
 where
-    ActualContainerT: PartialEq<ExpectedContainerT> + Debug + ?Sized,
     ExpectedContainerT: Debug,
 {
-    ContainerEqMatcher { expected, phantom: Default::default() }
+    ContainerEqMatcher { expected }
 }
 
-pub struct ContainerEqMatcher<ActualContainerT: ?Sized, ExpectedContainerT> {
+#[derive(MatcherBase)]
+pub struct ContainerEqMatcher<ExpectedContainerT> {
     expected: ExpectedContainerT,
-    phantom: PhantomData<ActualContainerT>,
 }
 
-impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT> Matcher
-    for ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
+impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
+    Matcher<ActualContainerT> for ContainerEqMatcher<ExpectedContainerT>
 where
-    ActualElementT: PartialEq<ExpectedElementT> + Debug + ?Sized,
-    ActualContainerT: PartialEq<ExpectedContainerT> + Debug + ?Sized,
+    ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Debug + Copy,
+    ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Debug + Copy,
     ExpectedElementT: Debug,
     ExpectedContainerT: Debug,
-    for<'a> &'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
+    ActualContainerT: IntoIterator<Item = ActualElementT>,
     for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
 {
-    type ActualT = ActualContainerT;
-
-    fn matches(&self, actual: &ActualContainerT) -> MatcherResult {
-        (*actual == self.expected).into()
+    fn matches(&self, actual: ActualContainerT) -> MatcherResult {
+        (actual == &self.expected).into()
     }
 
-    fn explain_match(&self, actual: &ActualContainerT) -> Description {
+    fn explain_match(&self, actual: ActualContainerT) -> Description {
         build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into()
     }
 
@@ -132,20 +90,32 @@
     }
 }
 
-impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
-    ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
+impl<ExpectedElementT, ExpectedContainerT> ContainerEqMatcher<ExpectedContainerT>
 where
-    ActualElementT: PartialEq<ExpectedElementT> + ?Sized,
-    ActualContainerT: PartialEq<ExpectedContainerT> + ?Sized,
-    for<'a> &'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
     for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
 {
-    fn get_missing_items(&self, actual: &ActualContainerT) -> Vec<&ExpectedElementT> {
-        self.expected.into_iter().filter(|&i| !actual.into_iter().any(|j| j == i)).collect()
+    fn get_missing_items<ActualElementT, ActualContainerT>(
+        &self,
+        actual: ActualContainerT,
+    ) -> Vec<&'_ ExpectedElementT>
+    where
+        ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Copy,
+        ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Copy,
+        ActualContainerT: IntoIterator<Item = ActualElementT>,
+    {
+        self.expected.into_iter().filter(|i| !actual.into_iter().any(|j| j == *i)).collect()
     }
 
-    fn get_unexpected_items<'a>(&self, actual: &'a ActualContainerT) -> Vec<&'a ActualElementT> {
-        actual.into_iter().filter(|&i| !self.expected.into_iter().any(|j| i == j)).collect()
+    fn get_unexpected_items<ActualElementT, ActualContainerT>(
+        &self,
+        actual: ActualContainerT,
+    ) -> Vec<ActualElementT>
+    where
+        ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Copy,
+        ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Copy,
+        ActualContainerT: IntoIterator<Item = ActualElementT>,
+    {
+        actual.into_iter().filter(|i| !self.expected.into_iter().any(|j| i == &j)).collect()
     }
 }
 
@@ -185,8 +155,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::container_eq;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::HashSet;
@@ -199,7 +168,7 @@
     #[test]
     fn container_eq_matches_array_with_slice() -> Result<()> {
         let value = &[1, 2, 3];
-        verify_that!(*value, container_eq([1, 2, 3]))
+        verify_that!(value, container_eq([1, 2, 3]))
     }
 
     #[test]
@@ -271,15 +240,15 @@
     }
 
     #[test]
-    fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references()
-    -> Result<()> {
+    fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references(
+    ) -> Result<()> {
         let vector = vec!["A string".to_string(), "Another string".to_string()];
         verify_that!(vector, container_eq(["A string", "Another string"]))
     }
 
     #[test]
-    fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references()
-    -> Result<()> {
+    fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references(
+    ) -> Result<()> {
         let actual = vec!["A string".to_string(), "Another string".to_string()];
         let matcher = container_eq(["A string"]);
 
diff --git a/crates/googletest/src/matchers/contains_matcher.rs b/crates/googletest/src/matchers/contains_matcher.rs
index 1a27ce0..3ce60ec 100644
--- a/crates/googletest/src/matchers/contains_matcher.rs
+++ b/crates/googletest/src/matchers/contains_matcher.rs
@@ -14,11 +14,12 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
-/// Matches an iterable type whose elements contain a value matched by `inner`.
+/// Matches an [`IntoIterator`] type whose elements contain a value matched by
+/// `inner`.
 ///
 /// By default, this matches a container with any number of elements matched
 /// by `inner`. Use the method [`ContainsMatcher::times`] to constrain the
@@ -28,11 +29,11 @@
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// verify_that!(["Some value"], contains(eq("Some value")))?;  // Passes
-/// verify_that!(vec!["Some value"], contains(eq("Some value")))?;  // Passes
+/// verify_that!(vec!["Some value"], contains(eq(&"Some value")))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail_1() -> Result<()> {
-/// verify_that!([] as [String; 0], contains(eq("Some value")))?;   // Fails
+/// verify_that!([] as [&String; 0], contains(eq("Some value")))?;   // Fails
 /// #     Ok(())
 /// # }
 /// # fn should_fail_2() -> Result<()> {
@@ -43,19 +44,19 @@
 /// # should_fail_1().unwrap_err();
 /// # should_fail_2().unwrap_err();
 /// ```
-pub fn contains<T, InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<T, InnerMatcherT> {
-    ContainsMatcher { inner, count: None, phantom: Default::default() }
+pub fn contains<InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<InnerMatcherT> {
+    ContainsMatcher { inner, count: None }
 }
 
 /// A matcher which matches a container containing one or more elements a given
 /// inner [`Matcher`] matches.
-pub struct ContainsMatcher<T, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct ContainsMatcher<InnerMatcherT> {
     inner: InnerMatcherT,
-    count: Option<Box<dyn Matcher<ActualT = usize>>>,
-    phantom: PhantomData<T>,
+    count: Option<Box<dyn Matcher<usize>>>,
 }
 
-impl<T, InnerMatcherT> ContainsMatcher<T, InnerMatcherT> {
+impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
     /// Configures this instance to match containers which contain a number of
     /// matching items matched by `count`.
     ///
@@ -68,7 +69,7 @@
     ///
     /// One can also use `times(eq(0))` to test for the *absence* of an item
     /// matching the expected value.
-    pub fn times(mut self, count: impl Matcher<ActualT = usize> + 'static) -> Self {
+    pub fn times(mut self, count: impl Matcher<usize> + 'static) -> Self {
         self.count = Some(Box::new(count));
         self
     }
@@ -84,16 +85,14 @@
 //  because val is dropped before matcher but the trait bound requires that
 //  the argument to matches outlive the matcher. It works fine if one defines
 //  val before matcher.
-impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>, ContainerT: Debug> Matcher
-    for ContainsMatcher<ContainerT, InnerMatcherT>
+impl<T: Debug + Copy, InnerMatcherT: Matcher<T>, ContainerT: Debug + Copy> Matcher<ContainerT>
+    for ContainsMatcher<InnerMatcherT>
 where
-    for<'a> &'a ContainerT: IntoIterator<Item = &'a T>,
+    ContainerT: IntoIterator<Item = T>,
 {
-    type ActualT = ContainerT;
-
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+    fn matches(&self, actual: ContainerT) -> MatcherResult {
         if let Some(count) = &self.count {
-            count.matches(&self.count_matches(actual))
+            count.matches(self.count_matches(actual))
         } else {
             for v in actual.into_iter() {
                 if self.inner.matches(v).into() {
@@ -104,7 +103,7 @@
         }
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    fn explain_match(&self, actual: ContainerT) -> Description {
         let count = self.count_matches(actual);
         match (count, &self.count) {
             (_, Some(_)) => format!("which contains {} matching elements", count).into(),
@@ -140,11 +139,11 @@
     }
 }
 
-impl<ActualT, InnerMatcherT> ContainsMatcher<ActualT, InnerMatcherT> {
-    fn count_matches<T: Debug, ContainerT>(&self, actual: &ContainerT) -> usize
+impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
+    fn count_matches<T: Debug + Copy, ContainerT>(&self, actual: ContainerT) -> usize
     where
-        for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
-        InnerMatcherT: Matcher<ActualT = T>,
+        ContainerT: IntoIterator<Item = T>,
+        InnerMatcherT: Matcher<T>,
     {
         let mut count = 0;
         for v in actual.into_iter() {
@@ -158,13 +157,12 @@
 
 #[cfg(test)]
 mod tests {
-    use super::{contains, ContainsMatcher};
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
 
     #[test]
     fn contains_matches_singleton_slice_with_value() -> Result<()> {
-        let matcher = contains(eq(1));
+        let matcher = contains(eq(&1));
 
         let result = matcher.matches(&vec![1]);
 
@@ -173,7 +171,7 @@
 
     #[test]
     fn contains_matches_singleton_vec_with_value() -> Result<()> {
-        let matcher = contains(eq(1));
+        let matcher = contains(eq(&1));
 
         let result = matcher.matches(&vec![1]);
 
@@ -182,7 +180,7 @@
 
     #[test]
     fn contains_matches_two_element_slice_with_value() -> Result<()> {
-        let matcher = contains(eq(1));
+        let matcher = contains(eq(&1));
 
         let result = matcher.matches(&[0, 1]);
 
@@ -191,7 +189,7 @@
 
     #[test]
     fn contains_does_not_match_singleton_slice_with_wrong_value() -> Result<()> {
-        let matcher = contains(eq(1));
+        let matcher = contains(eq(&1));
 
         let result = matcher.matches(&[0]);
 
@@ -200,16 +198,16 @@
 
     #[test]
     fn contains_does_not_match_empty_slice() -> Result<()> {
-        let matcher = contains(eq::<i32, _>(1));
+        let matcher = contains(eq(&1));
 
-        let result = matcher.matches(&[]);
+        let result = matcher.matches(&[1; 0]);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
     fn contains_matches_slice_with_repeated_value() -> Result<()> {
-        let matcher = contains(eq(1)).times(eq(2));
+        let matcher = contains(eq(&1)).times(eq(2));
 
         let result = matcher.matches(&[1, 1]);
 
@@ -218,7 +216,7 @@
 
     #[test]
     fn contains_does_not_match_slice_with_too_few_of_value() -> Result<()> {
-        let matcher = contains(eq(1)).times(eq(2));
+        let matcher = contains(eq(&1)).times(eq(2));
 
         let result = matcher.matches(&[0, 1]);
 
@@ -227,7 +225,7 @@
 
     #[test]
     fn contains_does_not_match_slice_with_too_many_of_value() -> Result<()> {
-        let matcher = contains(eq(1)).times(eq(1));
+        let matcher = contains(eq(&1)).times(eq(1));
 
         let result = matcher.matches(&[1, 1]);
 
@@ -236,20 +234,20 @@
 
     #[test]
     fn contains_formats_without_multiplicity_by_default() -> Result<()> {
-        let matcher: ContainsMatcher<Vec<i32>, _> = contains(eq(1));
+        let matcher = contains(eq(&1));
 
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("contains at least one element which is equal to 1"))
         )
     }
 
     #[test]
     fn contains_formats_with_multiplicity_when_specified() -> Result<()> {
-        let matcher: ContainsMatcher<Vec<i32>, _> = contains(eq(1)).times(eq(2));
+        let matcher = contains(eq(&1)).times(eq(2));
 
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("contains n elements which is equal to 1\n  where n is equal to 2"))
         )
     }
@@ -257,7 +255,7 @@
     #[test]
     fn contains_mismatch_shows_number_of_times_element_was_found() -> Result<()> {
         verify_that!(
-            contains(eq(3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
+            contains(eq(&3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
             displays_as(eq("which contains 2 matching elements"))
         )
     }
@@ -265,7 +263,7 @@
     #[test]
     fn contains_mismatch_shows_when_matches() -> Result<()> {
         verify_that!(
-            contains(eq(3)).explain_match(&vec![1, 2, 3, 3]),
+            contains(eq(&3)).explain_match(&vec![1, 2, 3, 3]),
             displays_as(eq("which contains a matching element"))
         )
     }
@@ -273,7 +271,7 @@
     #[test]
     fn contains_mismatch_shows_when_no_matches() -> Result<()> {
         verify_that!(
-            contains(eq(3)).explain_match(&vec![1, 2]),
+            contains(eq(&3)).explain_match(&vec![1, 2]),
             displays_as(eq("which does not contain a matching element"))
         )
     }
diff --git a/crates/googletest/src/matchers/contains_regex_matcher.rs b/crates/googletest/src/matchers/contains_regex_matcher.rs
index 6f68168..61a2d30 100644
--- a/crates/googletest/src/matchers/contains_regex_matcher.rs
+++ b/crates/googletest/src/matchers/contains_regex_matcher.rs
@@ -13,10 +13,9 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use regex::Regex;
 use std::fmt::Debug;
-use std::marker::PhantomData;
 use std::ops::Deref;
 
 /// Matches a string containing a substring which matches the given regular
@@ -47,18 +46,9 @@
 ///
 /// Panics if the given `pattern` is not a syntactically valid regular
 /// expression.
-// N.B. This returns the concrete type rather than an impl Matcher so that it
-// can act simultaneously as a Matcher<str> and a Matcher<String>. Otherwise the
-// compiler treats it as a Matcher<str> only and the code
-//   verify_that!("Some value".to_string(), contains_regex(".*value"))?;
-// doesn't compile.
-pub fn contains_regex<ActualT: ?Sized, PatternT: Deref<Target = str>>(
-    pattern: PatternT,
-) -> ContainsRegexMatcher<ActualT> {
-    ContainsRegexMatcher {
-        regex: Regex::new(pattern.deref()).unwrap(),
-        phantom: Default::default(),
-    }
+#[track_caller]
+pub fn contains_regex<PatternT: Deref<Target = str>>(pattern: PatternT) -> ContainsRegexMatcher {
+    ContainsRegexMatcher { regex: Regex::new(pattern.deref()).unwrap() }
 }
 
 /// A matcher matching a string-like type containing a substring matching a
@@ -66,15 +56,13 @@
 ///
 /// Intended only to be used from the function [`contains_regex`] only.
 /// Should not be referenced by code outside this library.
-pub struct ContainsRegexMatcher<ActualT: ?Sized> {
+#[derive(MatcherBase)]
+pub struct ContainsRegexMatcher {
     regex: Regex,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ActualT: AsRef<str> + Debug + ?Sized> Matcher for ContainsRegexMatcher<ActualT> {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
+impl<ActualT: AsRef<str> + Debug + Copy> Matcher<ActualT> for ContainsRegexMatcher {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         self.regex.is_match(actual.as_ref()).into()
     }
 
@@ -92,8 +80,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::{contains_regex, ContainsRegexMatcher};
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
 
     #[test]
@@ -139,10 +126,10 @@
 
     #[test]
     fn contains_regex_displays_quoted_debug_of_pattern() -> Result<()> {
-        let matcher: ContainsRegexMatcher<&str> = contains_regex("\n");
+        let matcher = contains_regex("\n");
 
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("contains the regular expression \"\\n\""))
         )
     }
diff --git a/crates/googletest/src/matchers/derefs_to_matcher.rs b/crates/googletest/src/matchers/derefs_to_matcher.rs
new file mode 100644
index 0000000..2c9082e
--- /dev/null
+++ b/crates/googletest/src/matchers/derefs_to_matcher.rs
@@ -0,0 +1,99 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherBase, MatcherResult},
+};
+use std::{fmt::Debug, ops::Deref};
+
+/// Dereferences the `actual` value and verifies that the returned reference
+/// matches the `inner` matcher.
+///
+/// ```
+/// # use googletest::{matchers::{derefs_to, eq}, verify_that};
+/// verify_that!(Box::new(123), derefs_to(eq(&123)))
+/// #    .unwrap()
+/// ```
+pub fn derefs_to<Inner>(inner: Inner) -> DerefsTo<Inner> {
+    DerefsTo { inner }
+}
+
+/// A matcher which derefs a value and verifies that the result matches the
+/// `inner` matcher.
+///
+/// See [`derefs_to`].
+#[derive(MatcherBase)]
+pub struct DerefsTo<InnerT> {
+    pub(crate) inner: InnerT,
+}
+
+impl<'a, ActualT, ExpectedT, Inner> Matcher<&'a ActualT> for DerefsTo<Inner>
+where
+    ActualT: Deref<Target = ExpectedT> + Debug,
+    ExpectedT: Copy + Debug + 'a,
+    Inner: Matcher<&'a ExpectedT>,
+{
+    fn matches(&self, actual: &'a ActualT) -> MatcherResult {
+        self.inner.matches(actual.deref())
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        self.inner.describe(matcher_result)
+    }
+
+    fn explain_match(&self, actual: &'a ActualT) -> Description {
+        self.inner.explain_match(actual.deref())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::rc::Rc;
+
+    use crate::prelude::*;
+    use indoc::indoc;
+
+    #[test]
+    fn deref_to_matches_box_of_int_with_int() -> Result<()> {
+        let actual = Box::new(123);
+        verify_that!(actual, derefs_to(eq(&123)))
+    }
+
+    #[test]
+    fn deref_to_matches_rc_of_int_with_int() -> Result<()> {
+        verify_that!(Rc::new(123), derefs_to(eq(&123)))
+    }
+
+    #[test]
+    fn deref_to_combines_with_points_to_for_copy() -> Result<()> {
+        verify_that!(Rc::new(123), derefs_to(points_to(eq(123))))
+    }
+
+    #[test]
+    fn match_explanation_references_actual_value() -> Result<()> {
+        let actual = Box::new(1);
+        let result = verify_that!(actual, derefs_to(eq(&0)));
+
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                "
+                    Actual: 1,
+                      which isn't equal to 0
+                "
+            ))))
+        )
+    }
+}
diff --git a/crates/googletest/src/matchers/disjunction_matcher.rs b/crates/googletest/src/matchers/disjunction_matcher.rs
index dd56be2..77e18f6 100644
--- a/crates/googletest/src/matchers/disjunction_matcher.rs
+++ b/crates/googletest/src/matchers/disjunction_matcher.rs
@@ -17,48 +17,81 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
 use std::fmt::Debug;
 
-/// Matcher created by [`Matcher::or`].
+/// Matcher created by [`Matcher::or`] and [`any!`].
 ///
+/// Both [`Matcher::or`] and [`any!`] nest on m1. In other words,
+/// both `x.or(y).or(z)` and `any![x, y, z]` produce:
+/// ```ignore
+/// DisjunctionMatcher {
+///     m1: DisjunctionMatcher {
+///         m1: x, m2: y
+///     },
+///     m2: z
+/// }
+/// ```
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
+#[derive(MatcherBase)]
 pub struct DisjunctionMatcher<M1, M2> {
     m1: M1,
     m2: M2,
 }
 
 impl<M1, M2> DisjunctionMatcher<M1, M2> {
-    pub(crate) fn new(m1: M1, m2: M2) -> Self {
+    pub fn new(m1: M1, m2: M2) -> Self {
         Self { m1, m2 }
     }
 }
 
-impl<M1: Matcher, M2: Matcher<ActualT = M1::ActualT>> Matcher for DisjunctionMatcher<M1, M2>
-where
-    M1::ActualT: Debug,
-{
-    type ActualT = M1::ActualT;
-
-    fn matches(&self, actual: &M1::ActualT) -> MatcherResult {
+impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for DisjunctionMatcher<M1, M2> {
+    fn matches(&self, actual: T) -> MatcherResult {
         match (self.m1.matches(actual), self.m2.matches(actual)) {
             (MatcherResult::NoMatch, MatcherResult::NoMatch) => MatcherResult::NoMatch,
             _ => MatcherResult::Match,
         }
     }
 
-    fn explain_match(&self, actual: &M1::ActualT) -> Description {
-        Description::new()
-            .nested(self.m1.explain_match(actual))
-            .text("and")
-            .nested(self.m2.explain_match(actual))
+    fn explain_match(&self, actual: T) -> Description {
+        match (self.m1.matches(actual), self.m2.matches(actual)) {
+            (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
+            (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
+            (_, _) => {
+                let m1_description = self.m1.explain_match(actual);
+                if m1_description.is_disjunction_description() {
+                    m1_description.nested(self.m2.explain_match(actual))
+                } else {
+                    Description::new()
+                        .bullet_list()
+                        .collect([m1_description, self.m2.explain_match(actual)])
+                        .disjunction_description()
+                }
+            }
+        }
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
-        format!("{}, or {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result))
-            .into()
+        let m1_description = self.m1.describe(matcher_result);
+        if m1_description.is_disjunction_description() {
+            m1_description.push_in_last_nested(self.m2.describe(matcher_result))
+        } else {
+            let header = if matcher_result.into() {
+                "has at least one of the following properties:"
+            } else {
+                "has all of the following properties:"
+            };
+            Description::new()
+                .text(header)
+                .nested(
+                    Description::new()
+                        .bullet_list()
+                        .collect([m1_description, self.m2.describe(matcher_result)]),
+                )
+                .disjunction_description()
+        }
     }
 }
 
@@ -90,11 +123,12 @@
             err(displays_as(contains_substring(indoc!(
                 "
                 Value of: 1
-                Expected: never matches, or never matches
+                Expected: has at least one of the following properties:
+                  * never matches
+                  * never matches
                 Actual: 1,
-                    which is anything
-                  and
-                    which is anything
+                  * which is anything
+                  * which is anything
                 "
             ))))
         )
diff --git a/crates/googletest/src/matchers/display_matcher.rs b/crates/googletest/src/matchers/display_matcher.rs
index 51a5bff..9edccca 100644
--- a/crates/googletest/src/matchers/display_matcher.rs
+++ b/crates/googletest/src/matchers/display_matcher.rs
@@ -13,9 +13,8 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use std::fmt::{Debug, Display};
-use std::marker::PhantomData;
 
 /// Matches the string representation of types that implement `Display`.
 ///
@@ -23,29 +22,31 @@
 /// let result: impl Display = ...;
 /// verify_that!(result, displays_as(eq(format!("{}", result))))?;
 /// ```
-pub fn displays_as<T: Debug + Display, InnerMatcher: Matcher<ActualT = String>>(
+pub fn displays_as<InnerMatcher: for<'a> Matcher<&'a str>>(
     inner: InnerMatcher,
-) -> impl Matcher<ActualT = T> {
-    DisplayMatcher::<T, _> { inner, phantom: Default::default() }
+) -> DisplayMatcher<InnerMatcher> {
+    DisplayMatcher { inner }
 }
 
-struct DisplayMatcher<T, InnerMatcher: Matcher> {
+#[derive(MatcherBase)]
+pub struct DisplayMatcher<InnerMatcher> {
     inner: InnerMatcher,
-    phantom: PhantomData<T>,
 }
 
-impl<T: Debug + Display, InnerMatcher: Matcher<ActualT = String>> Matcher
-    for DisplayMatcher<T, InnerMatcher>
+impl<T: Debug + Display + Copy, InnerMatcher: for<'a> Matcher<&'a str>> Matcher<T>
+    for DisplayMatcher<InnerMatcher>
 {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+    fn matches(&self, actual: T) -> MatcherResult {
         self.inner.matches(&format!("{actual}"))
     }
 
-    fn explain_match(&self, actual: &T) -> Description {
-        format!("which displays as a string {}", self.inner.explain_match(&format!("{actual}")))
-            .into()
+    fn explain_match(&self, actual: T) -> Description {
+        format!(
+            "which displays as {:?} {}",
+            actual.to_string(),
+            self.inner.explain_match(&format!("{actual}"))
+        )
+        .into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -65,7 +66,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::displays_as;
     use crate::prelude::*;
     use indoc::indoc;
     use std::fmt::{Debug, Display, Error, Formatter};
@@ -110,7 +110,8 @@
             err(displays_as(contains_substring(indoc!(
                 "
                   Actual: \"123\\n234\",
-                    which displays as a string which isn't equal to \"123\\n345\"
+                    which displays as \"123\\n234\" which isn't equal to \"123\\n345\"
+                    
                     Difference(-actual / +expected):
                      123
                     -234
diff --git a/crates/googletest/src/matchers/each_matcher.rs b/crates/googletest/src/matchers/each_matcher.rs
index 08a0742..6b76432 100644
--- a/crates/googletest/src/matchers/each_matcher.rs
+++ b/crates/googletest/src/matchers/each_matcher.rs
@@ -13,77 +13,58 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
-use std::{fmt::Debug, marker::PhantomData};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
+use std::fmt::Debug;
 
 /// Matches a container all of whose elements are matched by the matcher
 /// `inner`.
 ///
-/// `T` can be any container such that `&T` implements `IntoIterator`. This
-/// includes `Vec`, arrays, and (dereferenced) slices.
+/// `T` must implement [`IntoIterator`]. This
+/// includes `&Vec`, arrays, and slices.
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # use std::collections::HashSet;
 /// # fn should_pass_1() -> Result<()> {
 /// let value = vec![1, 2, 3];
-/// verify_that!(value, each(gt(0)))?;  // Passes
+/// verify_that!(value, each(gt(&0)))?;  // Passes
 /// let array_value = [1, 2, 3];
 /// verify_that!(array_value, each(gt(0)))?;  // Passes
 /// let slice_value = &[1, 2, 3];
-/// verify_that!(*slice_value, each(gt(0)))?;  // Passes
+/// verify_that!(slice_value, each(gt(&0)))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
 /// #     let value = vec![1, 2, 3];
-/// verify_that!(value, each(lt(2)))?;  // Fails: 2 and 3 are not less than 2
+/// verify_that!(value, each(lt(&2)))?;  // Fails: 2 and 3 are not less than 2
 /// #     Ok(())
 /// # }
 ///
 /// # fn should_pass_2() -> Result<()> {
 /// let value: HashSet<i32> = [1, 2, 3].into();
-/// verify_that!(value, each(gt(0)))?;  // Passes
+/// verify_that!(value, each(gt(&0)))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass_1().unwrap();
 /// # should_fail().unwrap_err();
 /// # should_pass_2().unwrap();
 /// ```
-///
-/// One can also verify the contents of a slice by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, each(gt(0)))?;
-/// #     Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-pub fn each<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT>(
-    inner: MatcherT,
-) -> impl Matcher<ActualT = ActualT>
-where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
-    MatcherT: Matcher<ActualT = ElementT>,
-{
-    EachMatcher { inner, phantom: Default::default() }
+pub fn each<MatcherT>(inner: MatcherT) -> EachMatcher<MatcherT> {
+    EachMatcher { inner }
 }
 
-struct EachMatcher<ActualT: ?Sized, MatcherT> {
+#[derive(MatcherBase)]
+pub struct EachMatcher<MatcherT> {
     inner: MatcherT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT> Matcher for EachMatcher<ActualT, MatcherT>
+impl<ElementT: Debug + Copy, ActualT: Debug + Copy, MatcherT> Matcher<ActualT>
+    for EachMatcher<MatcherT>
 where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
-    MatcherT: Matcher<ActualT = ElementT>,
+    ActualT: IntoIterator<Item = ElementT>,
+    MatcherT: Matcher<ElementT>,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         for element in actual {
             if self.inner.matches(element).is_no_match() {
                 return MatcherResult::NoMatch;
@@ -92,7 +73,7 @@
         MatcherResult::Match
     }
 
-    fn explain_match(&self, actual: &ActualT) -> Description {
+    fn explain_match(&self, actual: ActualT) -> Description {
         let mut non_matching_elements = Vec::new();
         for (index, element) in actual.into_iter().enumerate() {
             if self.inner.matches(element).is_no_match() {
@@ -137,7 +118,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::each;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::HashSet;
@@ -145,19 +125,19 @@
     #[test]
     fn each_matches_empty_vec() -> Result<()> {
         let value: Vec<i32> = vec![];
-        verify_that!(value, each(gt(0)))
+        verify_that!(value, each(gt(&0)))
     }
 
     #[test]
     fn each_matches_vec_with_one_element() -> Result<()> {
         let value = vec![1];
-        verify_that!(value, each(gt(0)))
+        verify_that!(value, each(gt(&0)))
     }
 
     #[test]
     fn each_matches_vec_with_two_elements() -> Result<()> {
         let value = vec![1, 2];
-        verify_that!(value, each(gt(0)))
+        verify_that!(value, each(gt(&0)))
     }
 
     #[test]
@@ -169,24 +149,24 @@
     #[test]
     fn each_matches_hash_set_with_one_element() -> Result<()> {
         let value: HashSet<i32> = [1].into();
-        verify_that!(value, each(gt(0)))
+        verify_that!(value, each(gt(&0)))
     }
 
     #[test]
     fn each_does_not_match_when_first_element_does_not_match() -> Result<()> {
         let value = vec![0];
-        verify_that!(value, not(each(gt(1))))
+        verify_that!(value, not(each(gt(&1))))
     }
 
     #[test]
     fn each_does_not_match_when_second_element_does_not_match() -> Result<()> {
         let value = vec![2, 0];
-        verify_that!(value, not(each(gt(1))))
+        verify_that!(value, not(each(gt(&1))))
     }
 
     #[test]
     fn each_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
-        let result = verify_that!(vec![0, 2, 3], each(gt(0)));
+        let result = verify_that!(vec![0, 2, 3], each(gt(&0)));
 
         verify_that!(
             result,
@@ -202,7 +182,7 @@
 
     #[test]
     fn each_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
-        let result = verify_that!(vec![1, 0, 3], each(gt(0)));
+        let result = verify_that!(vec![1, 0, 3], each(gt(&0)));
 
         verify_that!(
             result,
@@ -218,7 +198,7 @@
 
     #[test]
     fn each_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
-        let result = verify_that!(vec![0, 1, 3], each(gt(1)));
+        let result = verify_that!(vec![0, 1, 3], each(gt(&1)));
 
         verify_that!(
             result,
@@ -235,7 +215,7 @@
     }
     #[test]
     fn each_shows_inner_explanation() -> Result<()> {
-        let result = verify_that!(vec![vec![1, 2], vec![1]], each(each(eq(1))));
+        let result = verify_that!(vec![vec![1, 2], vec![1]], each(each(eq(&1))));
 
         verify_that!(
             result,
diff --git a/crates/googletest/src/matchers/elements_are_matcher.rs b/crates/googletest/src/matchers/elements_are_matcher.rs
index 3a4b5b2..a1b926b 100644
--- a/crates/googletest/src/matchers/elements_are_matcher.rs
+++ b/crates/googletest/src/matchers/elements_are_matcher.rs
@@ -24,19 +24,18 @@
 ///
 /// ```
 /// # use googletest::prelude::*;
-/// verify_that!(vec![1, 2, 3], elements_are![eq(1), anything(), gt(0).and(lt(123))])
+/// verify_that!(vec![1, 2, 3], elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
 /// #    .unwrap();
 /// ```
 ///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a slice.
+/// More precisely, the actual value must implement [`IntoIterator`].
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// let vector = vec![1, 2, 3];
 /// let slice = vector.as_slice();
-/// verify_that!(*slice, elements_are![eq(1), anything(), gt(0).and(lt(123))])
+/// verify_that!(slice, elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
 /// #    .unwrap();
 /// ```
 ///
@@ -45,7 +44,7 @@
 ///
 /// ```
 /// # use googletest::prelude::*;
-///  verify_that!(vec![1, 2], [eq(1), eq(2)])
+///  verify_that!(vec![1, 2], [eq(&1), eq(&2)])
 /// #     .unwrap();
 /// ```
 ///
@@ -55,19 +54,25 @@
 ///
 /// ```compile_fail
 /// # use googletest::prelude::*;
-/// verify_that!(vec![vec![1,2], vec![3]], [[eq(1), eq(2)], [eq(3)]])
+/// verify_that!(vec![vec![1,2], vec![3]], [[eq(&1), eq(&2)], [eq(&3)]])
 /// # .unwrap();
 /// ```
 ///
 /// Use this instead:
 /// ```
 /// # use googletest::prelude::*;
-/// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(1), eq(2)], elements_are![eq(3)]])
+/// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(&1), eq(&2)], elements_are![eq(&3)]])
 /// # .unwrap();
 /// ```
 ///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
+///  If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(vec![1,2,3], elements_are![&1, lt(&1000), gt(&1)])
+/// #     .unwrap();
+/// ```
 ///
 /// Do not use this with unordered containers, since that will lead to flaky
 /// tests. Use
@@ -82,8 +87,12 @@
 #[doc(hidden)]
 macro_rules! __elements_are {
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre;
-        ElementsAre::new(vec![$(Box::new($matcher)),*])
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre::new(
+            vec![$(Box::new(
+                $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+                    $matcher
+                )
+            )),*])
     }}
 }
 
@@ -93,36 +102,34 @@
 #[doc(hidden)]
 pub mod internal {
     use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::{Matcher, MatcherBase, MatcherResult};
     use crate::matcher_support::zipped_iterator::zip;
-    use std::{fmt::Debug, marker::PhantomData};
+    use std::fmt::Debug;
 
     /// This struct is meant to be used only by the macro `elements_are!`.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
-    pub struct ElementsAre<'a, ContainerT: ?Sized, T: Debug> {
-        elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>,
-        phantom: PhantomData<ContainerT>,
+    #[derive(MatcherBase)]
+    pub struct ElementsAre<'a, T: Debug + Copy> {
+        elements: Vec<Box<dyn Matcher<T> + 'a>>,
     }
 
-    impl<'a, ContainerT: ?Sized, T: Debug> ElementsAre<'a, ContainerT, T> {
+    impl<'a, T: Debug + Copy> ElementsAre<'a, T> {
         /// Factory only intended for use in the macro `elements_are!`.
         ///
         /// **For internal use only. API stablility is not guaranteed!**
         #[doc(hidden)]
-        pub fn new(elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>) -> Self {
-            Self { elements, phantom: Default::default() }
+        pub fn new(elements: Vec<Box<dyn Matcher<T> + 'a>>) -> Self {
+            Self { elements }
         }
     }
 
-    impl<'a, T: Debug, ContainerT: Debug + ?Sized> Matcher for ElementsAre<'a, ContainerT, T>
+    impl<'a, T: Debug + Copy, ContainerT: Debug + Copy> Matcher<ContainerT> for ElementsAre<'a, T>
     where
-        for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
+        ContainerT: IntoIterator<Item = T>,
     {
-        type ActualT = ContainerT;
-
-        fn matches(&self, actual: &ContainerT) -> MatcherResult {
+        fn matches(&self, actual: ContainerT) -> MatcherResult {
             let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter());
             for (a, e) in zipped_iterator.by_ref() {
                 if e.matches(a).is_no_match() {
@@ -136,7 +143,7 @@
             }
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> Description {
+        fn explain_match(&self, actual: ContainerT) -> Description {
             let actual_iterator = actual.into_iter();
             let mut zipped_iterator = zip(actual_iterator, self.elements.iter());
             let mut mismatches = Vec::new();
diff --git a/crates/googletest/src/matchers/empty_matcher.rs b/crates/googletest/src/matchers/empty_matcher.rs
index 11cb675..7631fc8 100644
--- a/crates/googletest/src/matchers/empty_matcher.rs
+++ b/crates/googletest/src/matchers/empty_matcher.rs
@@ -14,15 +14,15 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches an empty container.
 ///
-/// `T` can be any container such that `&T` implements `IntoIterator`. For
-/// instance, `T` can be a common container like `Vec` and
-/// [`HashSet`][std::collections::HashSet].
+/// `T` can be any container that implements `IntoIterator`. For instance, `T`
+/// can be the reference of a common container like `&Vec` and
+/// [`&HashSet`][std::collections::HashSet].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -32,42 +32,24 @@
 /// verify_that!(value, empty())?;
 /// let value: HashSet<i32> = HashSet::new();
 /// verify_that!(value, empty())?;
-/// #     Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// One can also check whether a slice is empty by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # use std::collections::HashSet;
-/// # fn should_pass() -> Result<()> {
 /// let value: &[u32] = &[];
-/// verify_that!(*value, empty())?;
+/// verify_that!(value, empty())?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
 /// ```
-
-pub fn empty<T: Debug + ?Sized>() -> impl Matcher<ActualT = T>
-where
-    for<'a> &'a T: IntoIterator,
-{
-    EmptyMatcher { phantom: Default::default() }
+pub fn empty() -> EmptyMatcher {
+    EmptyMatcher
 }
 
-struct EmptyMatcher<T: ?Sized> {
-    phantom: PhantomData<T>,
-}
+#[derive(MatcherBase)]
+pub struct EmptyMatcher;
 
-impl<T: Debug + ?Sized> Matcher for EmptyMatcher<T>
+impl<T: Debug + Copy> Matcher<T> for EmptyMatcher
 where
-    for<'a> &'a T: IntoIterator,
+    T: IntoIterator,
 {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+    fn matches(&self, actual: T) -> MatcherResult {
         actual.into_iter().next().is_none().into()
     }
 
@@ -78,7 +60,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::empty;
     use crate::prelude::*;
     use std::collections::HashSet;
 
@@ -97,7 +78,7 @@
     #[test]
     fn empty_matcher_matches_empty_slice() -> Result<()> {
         let value: &[i32] = &[];
-        verify_that!(*value, empty())
+        verify_that!(value, empty())
     }
 
     #[test]
diff --git a/crates/googletest/src/matchers/eq_deref_of_matcher.rs b/crates/googletest/src/matchers/eq_deref_of_matcher.rs
deleted file mode 100644
index 320aafb..0000000
--- a/crates/googletest/src/matchers/eq_deref_of_matcher.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// 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.
-
-use crate::{
-    description::Description,
-    matcher::{Matcher, MatcherResult},
-    matcher_support::{edit_distance, summarize_diff::create_diff},
-};
-use std::{fmt::Debug, marker::PhantomData, ops::Deref};
-
-/// Matches a value equal (in the sense of `==`) to the dereferenced value of
-/// `expected`.
-///
-/// This is similar to [`eq`][crate::matchers::eq] but takes a reference or
-/// smart pointer to the expected value rather than consuming it. This is useful
-/// when:
-///
-///  * one has only a reference to the expected value, and
-///  * the expected value cannot or should not be copied or cloned to create an
-///    owned value from it.
-///
-/// ```
-/// # use googletest::{matchers::eq_deref_of, verify_that};
-/// #[derive(Debug, PartialEq)]
-/// struct NonCloneableStruct(i32);
-/// let expected = NonCloneableStruct(123);
-/// verify_that!(NonCloneableStruct(123), eq_deref_of(&expected))
-/// #    .unwrap()
-/// ```
-///
-/// **Note**: while one can use `eq_deref_of` with the configuration methods of
-/// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
-/// to configure string equality, it is not possible to do so when the input is
-/// a smart pointer to a string.
-///
-/// ```compile_fail
-/// # use googletest::{matchers::{eq_deref_of, str_matcher::StrMatcherConfigurator}, verify_that};
-/// verify_that!("A string", eq_deref_of(Box::new("A STRING")).ignoring_ascii_case()) // Does not compile
-/// #    .unwrap()
-/// ```
-///
-/// Otherwise, this has the same behaviour as [`eq`][crate::matchers::eq].
-pub fn eq_deref_of<ActualT: ?Sized, ExpectedRefT>(
-    expected: ExpectedRefT,
-) -> EqDerefOfMatcher<ActualT, ExpectedRefT> {
-    EqDerefOfMatcher { expected, phantom: Default::default() }
-}
-
-/// A matcher which matches a value equal to the derefenced value of `expected`.
-///
-/// See [`eq_deref_of`].
-pub struct EqDerefOfMatcher<ActualT: ?Sized, ExpectedRefT> {
-    pub(crate) expected: ExpectedRefT,
-    phantom: PhantomData<ActualT>,
-}
-
-impl<ActualT, ExpectedRefT, ExpectedT> Matcher for EqDerefOfMatcher<ActualT, ExpectedRefT>
-where
-    ActualT: Debug + ?Sized,
-    ExpectedRefT: Deref<Target = ExpectedT> + Debug,
-    ExpectedT: PartialEq<ActualT> + Debug,
-{
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
-        (self.expected.deref() == actual).into()
-    }
-
-    fn describe(&self, matcher_result: MatcherResult) -> Description {
-        match matcher_result {
-            MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
-            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
-        }
-    }
-
-    fn explain_match(&self, actual: &ActualT) -> Description {
-        format!(
-            "which {}{}",
-            &self.describe(self.matches(actual)),
-            create_diff(
-                &format!("{:#?}", actual),
-                &format!("{:#?}", self.expected.deref()),
-                edit_distance::Mode::Exact,
-            )
-        )
-        .into()
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::eq_deref_of;
-    use crate::prelude::*;
-    use indoc::indoc;
-
-    #[derive(Debug, PartialEq)]
-    struct NonCloneNonCopyStruct(i32);
-
-    #[test]
-    fn matches_value_with_ref_to_equal_value() -> Result<()> {
-        verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(&NonCloneNonCopyStruct(123)))
-    }
-
-    #[test]
-    fn matches_value_with_box_of_equal_value() -> Result<()> {
-        verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(Box::new(NonCloneNonCopyStruct(123))))
-    }
-
-    #[test]
-    fn does_not_match_value_with_non_equal_value() -> Result<()> {
-        verify_that!(NonCloneNonCopyStruct(123), not(eq_deref_of(&NonCloneNonCopyStruct(234))))
-    }
-
-    #[test]
-    fn shows_structured_diff() -> Result<()> {
-        #[derive(Debug, PartialEq)]
-        struct Strukt {
-            int: i32,
-            string: String,
-        }
-
-        let result = verify_that!(
-            Strukt { int: 123, string: "something".into() },
-            eq_deref_of(Box::new(Strukt { int: 321, string: "someone".into() }))
-        );
-        verify_that!(
-            result,
-            err(displays_as(contains_substring(indoc! {
-            "
-            Actual: Strukt { int: 123, string: \"something\" },
-              which isn't equal to Strukt { int: 321, string: \"someone\" }
-              Difference(-actual / +expected):
-               Strukt {
-              -    int: 123,
-              +    int: 321,
-              -    string: \"something\",
-              +    string: \"someone\",
-               }
-            "})))
-        )
-    }
-}
diff --git a/crates/googletest/src/matchers/eq_matcher.rs b/crates/googletest/src/matchers/eq_matcher.rs
index 985307f..c39b79b 100644
--- a/crates/googletest/src/matchers/eq_matcher.rs
+++ b/crates/googletest/src/matchers/eq_matcher.rs
@@ -13,11 +13,11 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use crate::matcher_support::edit_distance;
 use crate::matcher_support::summarize_diff::create_diff;
 
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a value equal (in the sense of `==`) to `expected`.
 ///
@@ -71,23 +71,21 @@
 /// options on how equality is checked through the
 /// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
 /// extension trait, which is implemented for this matcher.
-pub fn eq<A: ?Sized, T>(expected: T) -> EqMatcher<A, T> {
-    EqMatcher { expected, phantom: Default::default() }
+pub fn eq<T>(expected: T) -> EqMatcher<T> {
+    EqMatcher { expected }
 }
 
 /// A matcher which matches a value equal to `expected`.
 ///
 /// See [`eq`].
-pub struct EqMatcher<A: ?Sized, T> {
+#[derive(MatcherBase)]
+pub struct EqMatcher<T> {
     pub(crate) expected: T,
-    phantom: PhantomData<A>,
 }
 
-impl<T: Debug, A: Debug + ?Sized + PartialEq<T>> Matcher for EqMatcher<A, T> {
-    type ActualT = A;
-
-    fn matches(&self, actual: &A) -> MatcherResult {
-        (*actual == self.expected).into()
+impl<T: Debug, A: Debug + Copy + PartialEq<T>> Matcher<A> for EqMatcher<T> {
+    fn matches(&self, actual: A) -> MatcherResult {
+        (actual == self.expected).into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -97,10 +95,10 @@
         }
     }
 
-    fn explain_match(&self, actual: &A) -> Description {
+    fn explain_match(&self, actual: A) -> Description {
         let expected_debug = format!("{:#?}", self.expected);
         let actual_debug = format!("{:#?}", actual);
-        let description = self.describe(self.matches(actual));
+        let description = Matcher::<A>::describe(self, self.matches(actual));
 
         let diff = if is_multiline_string_debug(&actual_debug)
             && is_multiline_string_debug(&expected_debug)
@@ -118,7 +116,11 @@
             create_diff(&actual_debug, &expected_debug, edit_distance::Mode::Exact)
         };
 
-        format!("which {description}{diff}").into()
+        if diff.is_empty() {
+            format!("which {description}").into()
+        } else {
+            format!("which {description}\n\n{diff}").into()
+        }
     }
 }
 
@@ -135,7 +137,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::eq;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -171,7 +172,7 @@
 
         let result = verify_that!(
             Strukt { int: 123, string: "something".into() },
-            eq(Strukt { int: 321, string: "someone".into() })
+            eq(&Strukt { int: 321, string: "someone".into() })
         );
         verify_that!(
             result,
@@ -179,6 +180,7 @@
             "
             Actual: Strukt { int: 123, string: \"something\" },
               which isn't equal to Strukt { int: 321, string: \"someone\" }
+              
               Difference(-actual / +expected):
                Strukt {
               -    int: 123,
@@ -192,7 +194,7 @@
 
     #[test]
     fn eq_vec_debug_diff() -> Result<()> {
-        let result = verify_that!(vec![1, 2, 3], eq(vec![1, 3, 4]));
+        let result = verify_that!(vec![1, 2, 3], eq(&vec![1, 3, 4]));
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc! {
@@ -201,6 +203,7 @@
             Expected: is equal to [1, 3, 4]
             Actual: [1, 2, 3],
               which isn't equal to [1, 3, 4]
+              
               Difference(-actual / +expected):
                [
                    1,
@@ -214,7 +217,7 @@
 
     #[test]
     fn eq_vec_debug_diff_length_mismatch() -> Result<()> {
-        let result = verify_that!(vec![1, 2, 3, 4, 5], eq(vec![1, 3, 5]));
+        let result = verify_that!(vec![1, 2, 3, 4, 5], eq(&vec![1, 3, 5]));
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc! {
@@ -223,6 +226,7 @@
             Expected: is equal to [1, 3, 5]
             Actual: [1, 2, 3, 4, 5],
               which isn't equal to [1, 3, 5]
+              
               Difference(-actual / +expected):
                [
                    1,
@@ -237,13 +241,14 @@
 
     #[test]
     fn eq_debug_diff_common_lines_omitted() -> Result<()> {
-        let result = verify_that!((1..50).collect::<Vec<_>>(), eq((3..52).collect::<Vec<_>>()));
+        let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc! {
             "
             ],
               which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+              
               Difference(-actual / +expected):
                [
               -    1,
@@ -261,13 +266,14 @@
 
     #[test]
     fn eq_debug_diff_5_common_lines_not_omitted() -> Result<()> {
-        let result = verify_that!((1..8).collect::<Vec<_>>(), eq((3..10).collect::<Vec<_>>()));
+        let result = verify_that!((1..8).collect::<Vec<_>>(), eq(&(3..10).collect::<Vec<_>>()));
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc! {
             "
             Actual: [1, 2, 3, 4, 5, 6, 7],
               which isn't equal to [3, 4, 5, 6, 7, 8, 9]
+              
               Difference(-actual / +expected):
                [
               -    1,
@@ -285,13 +291,14 @@
 
     #[test]
     fn eq_debug_diff_start_common_lines_omitted() -> Result<()> {
-        let result = verify_that!((1..50).collect::<Vec<_>>(), eq((1..52).collect::<Vec<_>>()));
+        let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(1..52).collect::<Vec<_>>()));
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc! {
             "
             ],
               which isn't equal to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+              
               Difference(-actual / +expected):
                [
                    1,
@@ -306,13 +313,14 @@
 
     #[test]
     fn eq_debug_diff_end_common_lines_omitted() -> Result<()> {
-        let result = verify_that!((1..52).collect::<Vec<_>>(), eq((3..52).collect::<Vec<_>>()));
+        let result = verify_that!((1..52).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc! {
             "
             ],
               which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+              
               Difference(-actual / +expected):
                [
               -    1,
diff --git a/crates/googletest/src/matchers/err_matcher.rs b/crates/googletest/src/matchers/err_matcher.rs
index 3b10de4..aa88045 100644
--- a/crates/googletest/src/matchers/err_matcher.rs
+++ b/crates/googletest/src/matchers/err_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a `Result` containing `Err` with a value matched by `inner`.
 ///
@@ -38,28 +38,53 @@
 /// # should_fail_1().unwrap_err();
 /// # should_fail_2().unwrap_err();
 /// ```
-pub fn err<T: Debug, E: Debug>(
-    inner: impl Matcher<ActualT = E>,
-) -> impl Matcher<ActualT = std::result::Result<T, E>> {
-    ErrMatcher::<T, E, _> { inner, phantom_t: Default::default(), phantom_e: Default::default() }
+pub fn err<Inner>(inner: Inner) -> ErrMatcher<Inner> {
+    ErrMatcher { inner }
 }
 
-struct ErrMatcher<T, E, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct ErrMatcher<InnerMatcherT> {
     inner: InnerMatcherT,
-    phantom_t: PhantomData<T>,
-    phantom_e: PhantomData<E>,
 }
 
-impl<T: Debug, E: Debug, InnerMatcherT: Matcher<ActualT = E>> Matcher
-    for ErrMatcher<T, E, InnerMatcherT>
+impl<T: Debug + Copy, E: Debug + Copy, InnerMatcherT: Matcher<E>> Matcher<std::result::Result<T, E>>
+    for ErrMatcher<InnerMatcherT>
 {
-    type ActualT = std::result::Result<T, E>;
+    fn matches(&self, actual: std::result::Result<T, E>) -> MatcherResult {
+        actual.err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
+    }
 
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+    fn explain_match(&self, actual: std::result::Result<T, E>) -> Description {
+        match actual {
+            Err(e) => {
+                Description::new().text("which is an error").nested(self.inner.explain_match(e))
+            }
+            Ok(_) => "which is a success".into(),
+        }
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        match matcher_result {
+            MatcherResult::Match => {
+                format!("is an error which {}", self.inner.describe(MatcherResult::Match)).into()
+            }
+            MatcherResult::NoMatch => format!(
+                "is a success or is an error containing a value which {}",
+                self.inner.describe(MatcherResult::NoMatch)
+            )
+            .into(),
+        }
+    }
+}
+
+impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<&'a E>> Matcher<&'a std::result::Result<T, E>>
+    for ErrMatcher<InnerMatcherT>
+{
+    fn matches(&self, actual: &'a std::result::Result<T, E>) -> MatcherResult {
         actual.as_ref().err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    fn explain_match(&self, actual: &'a std::result::Result<T, E>) -> Description {
         match actual {
             Err(e) => {
                 Description::new().text("which is an error").nested(self.inner.explain_match(e))
@@ -84,8 +109,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::err;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -94,7 +118,7 @@
         let matcher = err(eq(1));
         let value: std::result::Result<i32, i32> = Err(1);
 
-        let result = matcher.matches(&value);
+        let result = matcher.matches(value);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -104,7 +128,7 @@
         let matcher = err(eq(1));
         let value: std::result::Result<i32, i32> = Err(0);
 
-        let result = matcher.matches(&value);
+        let result = matcher.matches(value);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -114,12 +138,30 @@
         let matcher = err(eq(1));
         let value: std::result::Result<i32, i32> = Ok(1);
 
-        let result = matcher.matches(&value);
+        let result = matcher.matches(value);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
+    fn err_matches_result_with_err_value_by_ref() -> Result<()> {
+        let value: std::result::Result<String, String> = Err("123".to_string());
+        verify_that!(value, err(eq("123")))
+    }
+
+    #[test]
+    fn err_does_not_match_result_with_wrong_err_value_by_ref() -> Result<()> {
+        let value: std::result::Result<String, String> = Err("321".to_string());
+        verify_that!(value, not(err(eq("123"))))
+    }
+
+    #[test]
+    fn err_does_not_match_result_with_ok_by_ref() -> Result<()> {
+        let value: std::result::Result<String, String> = Ok("123".to_string());
+        verify_that!(value, not(err(eq("123"))))
+    }
+
+    #[test]
     fn err_full_error_message() -> Result<()> {
         let result = verify_that!(Err::<i32, i32>(1), err(eq(2)));
 
@@ -138,15 +180,102 @@
     }
 
     #[test]
-    fn err_describe_matches() -> Result<()> {
-        let matcher = super::ErrMatcher::<i32, i32, _> {
-            inner: eq(1),
-            phantom_t: Default::default(),
-            phantom_e: Default::default(),
-        };
+    fn err_describe_match() -> Result<()> {
+        let matcher = err(eq(1));
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is an error which is equal to 1"))
         )
     }
+
+    #[test]
+    fn err_describe_no_match() -> Result<()> {
+        let matcher = err(eq(1));
+        verify_that!(
+            Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::NoMatch),
+            displays_as(eq(
+                "is a success or is an error containing a value which isn't equal to 1"
+            ))
+        )
+    }
+
+    #[test]
+    fn err_describe_match_by_ref() -> Result<()> {
+        let matcher = err(eq("123"));
+        verify_that!(
+            Matcher::<&std::result::Result<String, String>>::describe(
+                &matcher,
+                MatcherResult::Match
+            ),
+            displays_as(eq("is an error which is equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn err_describe_no_match_by_ref() -> Result<()> {
+        let matcher = err(eq("123"));
+        verify_that!(
+            Matcher::<&std::result::Result<String, String>>::describe(
+                &matcher,
+                MatcherResult::NoMatch
+            ),
+            displays_as(eq(
+                "is a success or is an error containing a value which isn't equal to \"123\""
+            ))
+        )
+    }
+
+    #[test]
+    fn err_explain_match_success() -> Result<()> {
+        let actual: std::result::Result<i32, i32> = Err(1);
+        let matcher = err(eq(1));
+        verify_that!(
+            matcher.explain_match(actual),
+            displays_as(eq("which is an error\n  which is equal to 1"))
+        )
+    }
+
+    #[test]
+    fn err_explain_match_fail() -> Result<()> {
+        let actual: std::result::Result<i32, i32> = Err(2);
+        let matcher = err(eq(1));
+        verify_that!(
+            matcher.explain_match(actual),
+            displays_as(eq("which is an error\n  which isn't equal to 1"))
+        )
+    }
+
+    #[test]
+    fn err_explain_match_ok() -> Result<()> {
+        let actual: std::result::Result<i32, i32> = Ok(1);
+        let matcher = err(eq(1));
+        verify_that!(matcher.explain_match(actual), displays_as(eq("which is a success")))
+    }
+
+    #[test]
+    fn err_explain_match_success_by_ref() -> Result<()> {
+        let actual: std::result::Result<String, String> = Err("123".to_string());
+        let matcher = err(eq("123"));
+        verify_that!(
+            matcher.explain_match(&actual),
+            displays_as(eq("which is an error\n  which is equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn err_explain_match_fail_by_ref() -> Result<()> {
+        let actual: std::result::Result<String, String> = Err("321".to_string());
+        let matcher = err(eq("123"));
+        verify_that!(
+            matcher.explain_match(&actual),
+            displays_as(eq("which is an error\n  which isn't equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn err_explain_match_ok_by_ref() -> Result<()> {
+        let actual: std::result::Result<String, String> = Ok("123".to_string());
+        let matcher = err(eq("123"));
+        verify_that!(matcher.explain_match(&actual), displays_as(eq("which is a success")))
+    }
 }
diff --git a/crates/googletest/src/matchers/field_matcher.rs b/crates/googletest/src/matchers/field_matcher.rs
index fc37ce6..58a55c6 100644
--- a/crates/googletest/src/matchers/field_matcher.rs
+++ b/crates/googletest/src/matchers/field_matcher.rs
@@ -33,7 +33,7 @@
 ///   int: i32
 /// }
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(IntField{int: 32}, field!(IntField.int, eq(32)))?;
+/// verify_that!(IntField{int: 32}, field!(&IntField.int, eq(32)))?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -46,7 +46,7 @@
 /// #[derive(Debug)]
 /// struct IntField(i32);
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(IntField(32), field!(IntField.0, eq(32)))?;
+/// verify_that!(IntField(32), field!(&IntField.0, eq(32)))?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -63,11 +63,11 @@
 ///     B,
 /// }
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(MyEnum::A(32), field!(MyEnum::A.0, eq(32)))?; // Passes
+/// verify_that!(MyEnum::A(32), field!(&MyEnum::A.0, eq(32)))?; // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
-/// verify_that!(MyEnum::B, field!(MyEnum::A.0, eq(32)))?; // Fails: wrong enum variant
+/// verify_that!(MyEnum::B, field!(&MyEnum::A.0, eq(32)))?; // Fails: wrong enum variant
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -83,7 +83,22 @@
 ///     pub struct AStruct(pub i32);
 /// }
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(a_module::AStruct(32), field!(a_module::AStruct.0, eq(32)))?;
+/// verify_that!(a_module::AStruct(32), field!(&a_module::AStruct.0, eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// If the inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// struct IntField {
+///   int: i32
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(IntField{int: 32}, field!(&IntField.int, 32))?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -105,6 +120,67 @@
 /// # }
 /// ```
 ///
+/// # Specification of the field pattern
+///
+/// The specification of the field follow the syntax: `(ref)? (&)?
+/// $TYPE.$FIELD`.
+/// The `&` allows to specify whether this matcher matches against an actual of
+/// type `$TYPE` (`$TYPE`` must implement `Copy`) or a `&$TYPE`.
+///
+/// For instance:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct{a_field: i32};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: 32}, field!(&AStruct.a_field, eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug, Clone, Copy)]
+/// pub struct AStruct{a_field: i32};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: 32}, field!(AStruct.a_field, eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// The `ref` allows to bind the field value by reference, which is required if
+/// the field type does not implement `Copy`.
+///
+/// For instance:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct{a_field: i32};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: 32}, field!(&AStruct.a_field, eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// If `field!` is qualified by both `&` and `ref`, they can both be omitted.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct{a_field: String};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: "32".into()}, field!(&AStruct.a_field, ref eq("32")))?;
+/// verify_that!(AStruct{a_field: "32".into()}, field!(AStruct.a_field, eq("32")))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
 /// See also the macro [`property`][crate::matchers::property] for an analogous
 /// mechanism to extract a datum by invoking a method.
 #[macro_export]
@@ -115,15 +191,16 @@
 
 // Internal-only macro created so that the macro definition does not appear in
 // generated documentation.
+// We cannot use `path` or `ty` to capture the type as we are terminating the
+// type with a . (dot).
 #[doc(hidden)]
 #[macro_export]
 macro_rules! field_internal {
-    ($($t:ident)::+.$field:tt, $m:expr) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
-        field_matcher(
-            |o| {
+    (&$($t:ident)::+.$field:tt, ref $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
+            |o: &_| {
                 match o {
-                    $($t)::* { $field: value, .. } => Some(value),
+                    &$($t)::* {$field: ref value, .. } => Some(value),
                     // The pattern below is unreachable if the type is a struct (as opposed to an
                     // enum). Since the macro can't know which it is, we always include it and just
                     // tell the compiler not to complain.
@@ -132,7 +209,37 @@
                 }
             },
             &stringify!($field),
-            $m)
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+    }};
+    (&$($t:ident)::+.$field:tt, $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
+            |o: &&_| {
+                match o {
+                    &$($t)::* {$field: value, .. } => Some(value),
+                    // The pattern below is unreachable if the type is a struct (as opposed to an
+                    // enum). Since the macro can't know which it is, we always include it and just
+                    // tell the compiler not to complain.
+                    #[allow(unreachable_patterns)]
+                    _ => None,
+                }
+            },
+            &stringify!($field),
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+    }};
+    ($($t:ident)::+.$field:tt, $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
+            |o: &_| {
+                match o {
+                    $($t)::* {$field: value, .. } => Some(value),
+                    // The pattern below is unreachable if the type is a struct (as opposed to an
+                    // enum). Since the macro can't know which it is, we always include it and just
+                    // tell the compiler not to complain.
+                    #[allow(unreachable_patterns)]
+                    _ => None,
+                }
+            },
+            &stringify!($field),
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
     }};
 }
 
@@ -143,7 +250,7 @@
 pub mod internal {
     use crate::{
         description::Description,
-        matcher::{Matcher, MatcherResult},
+        matcher::{Matcher, MatcherBase, MatcherResult},
     };
     use std::fmt::Debug;
 
@@ -152,26 +259,25 @@
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
-    pub fn field_matcher<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>>(
+    pub fn field_matcher<OuterT, InnerT, InnerMatcher>(
         field_accessor: fn(&OuterT) -> Option<&InnerT>,
         field_path: &'static str,
         inner: InnerMatcher,
-    ) -> impl Matcher<ActualT = OuterT> {
+    ) -> FieldMatcher<OuterT, InnerT, InnerMatcher> {
         FieldMatcher { field_accessor, field_path, inner }
     }
 
-    struct FieldMatcher<OuterT, InnerT, InnerMatcher> {
+    #[derive(MatcherBase)]
+    pub struct FieldMatcher<OuterT, InnerT, InnerMatcher> {
         field_accessor: fn(&OuterT) -> Option<&InnerT>,
         field_path: &'static str,
         inner: InnerMatcher,
     }
 
-    impl<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>> Matcher
-        for FieldMatcher<OuterT, InnerT, InnerMatcher>
+    impl<'a, OuterT: Debug + 'a, InnerT: Debug + 'a, InnerMatcher: Matcher<&'a InnerT>>
+        Matcher<&'a OuterT> for FieldMatcher<OuterT, InnerT, InnerMatcher>
     {
-        type ActualT = OuterT;
-
-        fn matches(&self, actual: &OuterT) -> MatcherResult {
+        fn matches(&self, actual: &'a OuterT) -> MatcherResult {
             if let Some(value) = (self.field_accessor)(actual) {
                 self.inner.matches(value)
             } else {
@@ -179,7 +285,7 @@
             }
         }
 
-        fn explain_match(&self, actual: &OuterT) -> Description {
+        fn explain_match(&self, actual: &'a OuterT) -> Description {
             if let Some(actual) = (self.field_accessor)(actual) {
                 format!(
                     "which has field `{}`, {}",
@@ -204,4 +310,41 @@
             .into()
         }
     }
+
+    impl<OuterT: Debug + Copy, InnerT: Debug + Copy, InnerMatcher: Matcher<InnerT>> Matcher<OuterT>
+        for FieldMatcher<OuterT, InnerT, InnerMatcher>
+    {
+        fn matches(&self, actual: OuterT) -> MatcherResult {
+            if let Some(value) = (self.field_accessor)(&actual) {
+                self.inner.matches(*value)
+            } else {
+                MatcherResult::NoMatch
+            }
+        }
+
+        fn explain_match(&self, actual: OuterT) -> Description {
+            if let Some(actual) = (self.field_accessor)(&actual) {
+                format!(
+                    "which has field `{}`, {}",
+                    self.field_path,
+                    self.inner.explain_match(*actual)
+                )
+                .into()
+            } else {
+                let formatted_actual_value = format!("{actual:?}");
+                let without_fields = formatted_actual_value.split('(').next().unwrap_or("");
+                let without_fields = without_fields.split('{').next().unwrap_or("").trim_end();
+                format!("which has the wrong enum variant `{without_fields}`").into()
+            }
+        }
+
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
+            format!(
+                "has field `{}`, which {}",
+                self.field_path,
+                self.inner.describe(matcher_result)
+            )
+            .into()
+        }
+    }
 }
diff --git a/crates/googletest/src/matchers/ge_matcher.rs b/crates/googletest/src/matchers/ge_matcher.rs
index cb2a91f..4919f2c 100644
--- a/crates/googletest/src/matchers/ge_matcher.rs
+++ b/crates/googletest/src/matchers/ge_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a value greater than or equal to (in the sense of `>=`) `expected`.
 ///
@@ -74,24 +74,20 @@
 ///
 /// You can find the standard library `PartialOrd` implementation in
 /// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn ge<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
-    expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
-    GeMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn ge<ExpectedT>(expected: ExpectedT) -> GeMatcher<ExpectedT> {
+    GeMatcher { expected }
 }
 
-struct GeMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct GeMatcher<ExpectedT> {
     expected: ExpectedT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
-    for GeMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+    for GeMatcher<ExpectedT>
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
-        (*actual >= self.expected).into()
+    fn matches(&self, actual: ActualT) -> MatcherResult {
+        (actual >= self.expected).into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -106,8 +102,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::ge;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::ffi::OsString;
@@ -122,7 +117,7 @@
     #[test]
     fn ge_does_not_match_smaller_i32() -> Result<()> {
         let matcher = ge(10);
-        let result = matcher.matches(&9);
+        let result = matcher.matches(9);
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -134,7 +129,7 @@
     #[test]
     fn ge_does_not_match_lesser_str() -> Result<()> {
         let matcher = ge("z");
-        let result = matcher.matches(&"a");
+        let result = matcher.matches("a");
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
diff --git a/crates/googletest/src/matchers/gt_matcher.rs b/crates/googletest/src/matchers/gt_matcher.rs
index b52c6e3..123201f 100644
--- a/crates/googletest/src/matchers/gt_matcher.rs
+++ b/crates/googletest/src/matchers/gt_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a value greater (in the sense of `>`) than `expected`.
 ///
@@ -74,24 +74,20 @@
 ///
 /// You can find the standard library `PartialOrd` implementation in
 /// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn gt<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
-    expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
-    GtMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn gt<ExpectedT: Debug>(expected: ExpectedT) -> GtMatcher<ExpectedT> {
+    GtMatcher { expected }
 }
 
-struct GtMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct GtMatcher<ExpectedT> {
     expected: ExpectedT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
-    for GtMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+    for GtMatcher<ExpectedT>
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
-        (*actual > self.expected).into()
+    fn matches(&self, actual: ActualT) -> MatcherResult {
+        (actual > self.expected).into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -106,8 +102,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::gt;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::ffi::OsString;
@@ -122,14 +117,14 @@
     #[test]
     fn gt_does_not_match_equal_i32() -> Result<()> {
         let matcher = gt(10);
-        let result = matcher.matches(&10);
+        let result = matcher.matches(10);
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
     fn gt_does_not_match_lower_i32() -> Result<()> {
         let matcher = gt(-50);
-        let result = matcher.matches(&-51);
+        let result = matcher.matches(-51);
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -141,7 +136,7 @@
     #[test]
     fn gt_does_not_match_lesser_str() -> Result<()> {
         let matcher = gt("B");
-        let result = matcher.matches(&"A");
+        let result = matcher.matches("A");
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -163,7 +158,7 @@
 
     #[test]
     fn gt_mismatch_combined_with_each() -> Result<()> {
-        let result = verify_that!(vec![19, 23, 11], each(gt(15)));
+        let result = verify_that!(vec![19, 23, 11], each(gt(&15)));
 
         verify_that!(
             result,
@@ -181,7 +176,7 @@
     #[test]
     fn gt_describe_matches() -> Result<()> {
         verify_that!(
-            gt::<i32, i32>(232).describe(MatcherResult::Match),
+            Matcher::<i32>::describe(&gt(232), MatcherResult::Match),
             displays_as(eq("is greater than 232"))
         )
     }
@@ -189,7 +184,7 @@
     #[test]
     fn gt_describe_does_not_match() -> Result<()> {
         verify_that!(
-            gt::<i32, i32>(232).describe(MatcherResult::NoMatch),
+            Matcher::<i32>::describe(&gt(232), MatcherResult::NoMatch),
             displays_as(eq("is less than or equal to 232"))
         )
     }
diff --git a/crates/googletest/src/matchers/has_entry_matcher.rs b/crates/googletest/src/matchers/has_entry_matcher.rs
index 7f9d2ad..b5cce71 100644
--- a/crates/googletest/src/matchers/has_entry_matcher.rs
+++ b/crates/googletest/src/matchers/has_entry_matcher.rs
@@ -13,31 +13,30 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use std::collections::HashMap;
 use std::fmt::Debug;
 use std::hash::Hash;
-use std::marker::PhantomData;
 
-/// Matches a HashMap containing the given `key` whose value is matched by the
-/// matcher `inner`.
+/// Matches a `&HashMap` containing the given `key` whose value is matched by
+/// the matcher `inner`.
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # use std::collections::HashMap;
 /// # fn should_pass() -> Result<()> {
 /// let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value, has_entry(0, eq(1)))?;  // Passes
+/// verify_that!(value, has_entry(0, eq(&1)))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail_1() -> Result<()> {
 /// # let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value, has_entry(1, gt(0)))?;  // Fails: value not matched
+/// verify_that!(value, has_entry(1, gt(&0)))?;  // Fails: value not matched
 /// #     Ok(())
 /// # }
 /// # fn should_fail_2() -> Result<()> {
 /// # let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value, has_entry(2, eq(0)))?;  // Fails: key not present
+/// verify_that!(value, has_entry(2, eq(&0)))?;  // Fails: key not present
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -45,42 +44,33 @@
 /// # should_fail_2().unwrap_err();
 /// ```
 ///
-/// Note: One could obtain the same effect by collecting entries into a `Vec`
-/// and using `contains`:
+/// Note: One could obtain the same effect by using `contains` and a
+/// `Matcher<(&Key, &Value)>`:
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # use std::collections::HashMap;
 /// # fn should_pass() -> Result<()> {
 /// let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value.into_iter().collect::<Vec<_>>(), contains(eq((0, 1))))?;
+/// verify_that!(value, contains(eq((&0, &1))))?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
 /// ```
-///
-/// However, `has_entry` will offer somewhat better diagnostic messages in the
-/// case of assertion failure. And it avoid the extra allocation hidden in the
-/// code above.
-pub fn has_entry<KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<ActualT = ValueT>>(
-    key: KeyT,
-    inner: MatcherT,
-) -> impl Matcher<ActualT = HashMap<KeyT, ValueT>> {
-    HasEntryMatcher { key, inner, phantom: Default::default() }
+pub fn has_entry<KeyT, MatcherT>(key: KeyT, inner: MatcherT) -> HasEntryMatcher<KeyT, MatcherT> {
+    HasEntryMatcher { key, inner }
 }
 
-struct HasEntryMatcher<KeyT, ValueT, MatcherT> {
+#[derive(MatcherBase)]
+pub struct HasEntryMatcher<KeyT, MatcherT> {
     key: KeyT,
     inner: MatcherT,
-    phantom: PhantomData<ValueT>,
 }
 
-impl<KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<ActualT = ValueT>> Matcher
-    for HasEntryMatcher<KeyT, ValueT, MatcherT>
+impl<'a, KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<&'a ValueT>>
+    Matcher<&'a HashMap<KeyT, ValueT>> for HasEntryMatcher<KeyT, MatcherT>
 {
-    type ActualT = HashMap<KeyT, ValueT>;
-
-    fn matches(&self, actual: &HashMap<KeyT, ValueT>) -> MatcherResult {
+    fn matches(&self, actual: &'a HashMap<KeyT, ValueT>) -> MatcherResult {
         if let Some(value) = actual.get(&self.key) {
             self.inner.matches(value)
         } else {
@@ -88,7 +78,7 @@
         }
     }
 
-    fn explain_match(&self, actual: &HashMap<KeyT, ValueT>) -> Description {
+    fn explain_match(&self, actual: &'a HashMap<KeyT, ValueT>) -> Description {
         if let Some(value) = actual.get(&self.key) {
             format!(
                 "which contains key {:?}, but is mapped to value {:#?}, {}",
@@ -123,7 +113,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::has_entry;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::HashMap;
@@ -131,30 +120,30 @@
     #[test]
     fn has_entry_does_not_match_empty_hash_map() -> Result<()> {
         let value: HashMap<i32, i32> = HashMap::new();
-        verify_that!(value, not(has_entry(0, eq(0))))
+        verify_that!(&value, not(has_entry(0, eq(&0))))
     }
 
     #[test]
     fn has_entry_matches_hash_map_with_value() -> Result<()> {
         let value: HashMap<i32, i32> = HashMap::from([(0, 0)]);
-        verify_that!(value, has_entry(0, eq(0)))
+        verify_that!(&value, has_entry(0, eq(&0)))
     }
 
     #[test]
     fn has_entry_does_not_match_hash_map_with_wrong_value() -> Result<()> {
         let value: HashMap<i32, i32> = HashMap::from([(0, 1)]);
-        verify_that!(value, not(has_entry(0, eq(0))))
+        verify_that!(&value, not(has_entry(0, eq(&0))))
     }
 
     #[test]
     fn has_entry_does_not_match_hash_map_with_wrong_key() -> Result<()> {
         let value: HashMap<i32, i32> = HashMap::from([(1, 0)]);
-        verify_that!(value, not(has_entry(0, eq(0))))
+        verify_that!(&value, not(has_entry(0, eq(&0))))
     }
 
     #[test]
     fn has_entry_shows_correct_message_when_key_is_not_present() -> Result<()> {
-        let result = verify_that!(HashMap::from([(0, 0)]), has_entry(1, eq(0)));
+        let result = verify_that!(HashMap::from([(0, 0)]), has_entry(1, eq(&0)));
 
         verify_that!(
             result,
@@ -171,7 +160,7 @@
 
     #[test]
     fn has_entry_shows_correct_message_when_key_has_non_matching_value() -> Result<()> {
-        let result = verify_that!(HashMap::from([(0, 0)]), has_entry(0, eq(1)));
+        let result = verify_that!(HashMap::from([(0, 0)]), has_entry(0, eq(&1)));
 
         verify_that!(
             result,
diff --git a/crates/googletest/src/matchers/is_encoded_string_matcher.rs b/crates/googletest/src/matchers/is_encoded_string_matcher.rs
index d2fb259..098658b 100644
--- a/crates/googletest/src/matchers/is_encoded_string_matcher.rs
+++ b/crates/googletest/src/matchers/is_encoded_string_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a byte sequence which is a UTF-8 encoded string matched by `inner`.
 ///
@@ -48,30 +48,26 @@
 /// # should_fail_1().unwrap_err();
 /// # should_fail_2().unwrap_err();
 /// ```
-pub fn is_utf8_string<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT>(
-    inner: InnerMatcherT,
-) -> impl Matcher<ActualT = ActualT>
+pub fn is_utf8_string<InnerMatcherT>(inner: InnerMatcherT) -> IsEncodedStringMatcher<InnerMatcherT>
 where
-    InnerMatcherT: Matcher<ActualT = String>,
+    InnerMatcherT: for<'a> Matcher<&'a str>,
 {
-    IsEncodedStringMatcher { inner, phantom: Default::default() }
+    IsEncodedStringMatcher { inner }
 }
 
-struct IsEncodedStringMatcher<ActualT, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct IsEncodedStringMatcher<InnerMatcherT> {
     inner: InnerMatcherT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher
-    for IsEncodedStringMatcher<ActualT, InnerMatcherT>
+impl<ActualT: AsRef<[u8]> + Debug + Copy, InnerMatcherT> Matcher<ActualT>
+    for IsEncodedStringMatcher<InnerMatcherT>
 where
-    InnerMatcherT: Matcher<ActualT = String>,
+    InnerMatcherT: for<'a> Matcher<&'a str>,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-        String::from_utf8(actual.as_ref().to_vec())
-            .map(|s| self.inner.matches(&s))
+    fn matches(&self, actual: ActualT) -> MatcherResult {
+        std::str::from_utf8(actual.as_ref())
+            .map(|s| self.inner.matches(s))
             .unwrap_or(MatcherResult::NoMatch)
     }
 
@@ -90,10 +86,10 @@
         }
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> Description {
-        match String::from_utf8(actual.as_ref().to_vec()) {
+    fn explain_match(&self, actual: ActualT) -> Description {
+        match std::str::from_utf8(actual.as_ref()) {
             Ok(s) => {
-                format!("which is a UTF-8 encoded string {}", self.inner.explain_match(&s)).into()
+                format!("which is a UTF-8 encoded string {}", self.inner.explain_match(s)).into()
             }
             Err(e) => format!("which is not a UTF-8 encoded string: {e}").into(),
         }
@@ -132,27 +128,27 @@
 
     #[test]
     fn has_correct_description_in_matched_case() -> Result<()> {
-        let matcher = is_utf8_string::<&[u8], _>(eq("A string"));
+        let matcher = is_utf8_string(eq("A string"));
 
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<&[u8]>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is a UTF-8 encoded string which is equal to \"A string\""))
         )
     }
 
     #[test]
     fn has_correct_description_in_not_matched_case() -> Result<()> {
-        let matcher = is_utf8_string::<&[u8], _>(eq("A string"));
+        let matcher = is_utf8_string(eq("A string"));
 
         verify_that!(
-            matcher.describe(MatcherResult::NoMatch),
+            Matcher::<&[u8]>::describe(&matcher, MatcherResult::NoMatch),
             displays_as(eq("is not a UTF-8 encoded string which is equal to \"A string\""))
         )
     }
 
     #[test]
     fn has_correct_explanation_in_matched_case() -> Result<()> {
-        let explanation = is_utf8_string(eq("A string")).explain_match(&"A string".as_bytes());
+        let explanation = is_utf8_string(eq("A string")).explain_match("A string".as_bytes());
 
         verify_that!(
             explanation,
@@ -162,15 +158,14 @@
 
     #[test]
     fn has_correct_explanation_when_byte_array_is_not_utf8_encoded() -> Result<()> {
-        let explanation = is_utf8_string(eq("A string")).explain_match(&&[192, 128, 0, 64]);
+        let explanation = is_utf8_string(eq("A string")).explain_match([192, 128, 0, 64]);
 
         verify_that!(explanation, displays_as(starts_with("which is not a UTF-8 encoded string: ")))
     }
 
     #[test]
     fn has_correct_explanation_when_inner_matcher_does_not_match() -> Result<()> {
-        let explanation =
-            is_utf8_string(eq("A string")).explain_match(&"Another string".as_bytes());
+        let explanation = is_utf8_string(eq("A string")).explain_match("Another string".as_bytes());
 
         verify_that!(
             explanation,
diff --git a/crates/googletest/src/matchers/is_matcher.rs b/crates/googletest/src/matchers/is_matcher.rs
index 336ce53..3748858 100644
--- a/crates/googletest/src/matchers/is_matcher.rs
+++ b/crates/googletest/src/matchers/is_matcher.rs
@@ -16,34 +16,29 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches precisely values matched by `inner`.
 ///
 /// The returned matcher produces a description prefixed by the string
 /// `description`. This is useful in contexts where the test assertion failure
 /// output must include the additional description.
-pub fn is<'a, ActualT: Debug + 'a, InnerMatcherT: Matcher<ActualT = ActualT> + 'a>(
-    description: &'a str,
-    inner: InnerMatcherT,
-) -> impl Matcher<ActualT = ActualT> + 'a {
-    IsMatcher { description, inner, phantom: Default::default() }
+pub fn is<InnerMatcherT>(description: &str, inner: InnerMatcherT) -> IsMatcher<'_, InnerMatcherT> {
+    IsMatcher { description, inner }
 }
 
-struct IsMatcher<'a, ActualT, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct IsMatcher<'a, InnerMatcherT> {
     description: &'a str,
     inner: InnerMatcherT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<'a, ActualT: Debug, InnerMatcherT: Matcher<ActualT = ActualT>> Matcher
-    for IsMatcher<'a, ActualT, InnerMatcherT>
+impl<'a, ActualT: Debug + Copy, InnerMatcherT: Matcher<ActualT>> Matcher<ActualT>
+    for IsMatcher<'a, InnerMatcherT>
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         self.inner.matches(actual)
     }
 
@@ -64,7 +59,7 @@
         }
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    fn explain_match(&self, actual: ActualT) -> Description {
         self.inner.explain_match(actual)
     }
 }
diff --git a/crates/googletest/src/matchers/is_nan_matcher.rs b/crates/googletest/src/matchers/is_nan_matcher.rs
index 0af4338..f6fae89 100644
--- a/crates/googletest/src/matchers/is_nan_matcher.rs
+++ b/crates/googletest/src/matchers/is_nan_matcher.rs
@@ -14,22 +14,21 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
 use num_traits::float::Float;
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a floating point value which is NaN.
-pub fn is_nan<T: Float + Debug>() -> impl Matcher<ActualT = T> {
-    IsNanMatcher::<T>(Default::default())
+pub fn is_nan() -> IsNanMatcher {
+    IsNanMatcher
 }
 
-struct IsNanMatcher<T>(PhantomData<T>);
+#[derive(MatcherBase)]
+pub struct IsNanMatcher;
 
-impl<T: Float + Debug> Matcher for IsNanMatcher<T> {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+impl<T: Float + Debug + Copy> Matcher<T> for IsNanMatcher {
+    fn matches(&self, actual: T) -> MatcherResult {
         actual.is_nan().into()
     }
 
@@ -40,7 +39,6 @@
 
 #[cfg(test)]
 mod tests {
-    use super::is_nan;
     use crate::prelude::*;
 
     #[test]
diff --git a/crates/googletest/src/matchers/le_matcher.rs b/crates/googletest/src/matchers/le_matcher.rs
index cfc5781..980c516 100644
--- a/crates/googletest/src/matchers/le_matcher.rs
+++ b/crates/googletest/src/matchers/le_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a value less than or equal to (in the sense of `<=`) `expected`.
 ///
@@ -74,24 +74,20 @@
 ///
 /// You can find the standard library `PartialOrd` implementation in
 /// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn le<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
-    expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
-    LeMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn le<ExpectedT>(expected: ExpectedT) -> LeMatcher<ExpectedT> {
+    LeMatcher { expected }
 }
 
-struct LeMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct LeMatcher<ExpectedT> {
     expected: ExpectedT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
-    for LeMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+    for LeMatcher<ExpectedT>
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
-        (*actual <= self.expected).into()
+    fn matches(&self, actual: ActualT) -> MatcherResult {
+        (actual <= self.expected).into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -104,8 +100,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::le;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::ffi::OsString;
@@ -120,7 +115,7 @@
     #[test]
     fn le_does_not_match_bigger_i32() -> Result<()> {
         let matcher = le(0);
-        let result = matcher.matches(&1);
+        let result = matcher.matches(1);
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -132,7 +127,7 @@
     #[test]
     fn le_does_not_match_bigger_str() -> Result<()> {
         let matcher = le("a");
-        let result = matcher.matches(&"z");
+        let result = matcher.matches("z");
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -186,7 +181,7 @@
         /// A custom "number" that is lower than all other numbers. The only
         /// things we define about this "special" number is `PartialOrd` and
         /// `PartialEq` against `u32`.
-        #[derive(Debug)]
+        #[derive(Debug, Clone, Copy)]
         struct VeryLowNumber {}
 
         impl std::cmp::PartialEq<u32> for VeryLowNumber {
diff --git a/crates/googletest/src/matchers/len_matcher.rs b/crates/googletest/src/matchers/len_matcher.rs
index be903c9..a48d32c 100644
--- a/crates/googletest/src/matchers/len_matcher.rs
+++ b/crates/googletest/src/matchers/len_matcher.rs
@@ -13,16 +13,15 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use crate::matcher_support::count_elements::count_elements;
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a container whose number of elements matches `expected`.
 ///
 /// This matches against a container over which one can iterate. This includes
-/// the standard Rust containers, arrays, and (when dereferenced) slices. More
-/// precisely, a shared borrow of the actual type must implement
-/// [`IntoIterator`].
+/// the standard Rust containers, arrays, and slices. More
+/// precisely, the actual type must implement [`IntoIterator`].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -49,26 +48,21 @@
 /// # }
 /// # should_pass().unwrap();
 /// ```
-pub fn len<T: Debug + ?Sized, E: Matcher<ActualT = usize>>(expected: E) -> impl Matcher<ActualT = T>
-where
-    for<'a> &'a T: IntoIterator,
-{
-    LenMatcher { expected, phantom: Default::default() }
+pub fn len<E>(expected: E) -> LenMatcher<E> {
+    LenMatcher { expected }
 }
 
-struct LenMatcher<T: ?Sized, E> {
+#[derive(MatcherBase)]
+pub struct LenMatcher<E> {
     expected: E,
-    phantom: PhantomData<T>,
 }
 
-impl<T: Debug + ?Sized, E: Matcher<ActualT = usize>> Matcher for LenMatcher<T, E>
+impl<T: Debug + Copy, E: Matcher<usize>> Matcher<T> for LenMatcher<E>
 where
-    for<'a> &'a T: IntoIterator,
+    T: IntoIterator,
 {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
-        self.expected.matches(&count_elements(actual))
+    fn matches(&self, actual: T) -> MatcherResult {
+        self.expected.matches(count_elements(actual))
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -83,25 +77,23 @@
         }
     }
 
-    fn explain_match(&self, actual: &T) -> Description {
+    fn explain_match(&self, actual: T) -> Description {
         let actual_size = count_elements(actual);
-        format!("which has length {}, {}", actual_size, self.expected.explain_match(&actual_size))
+        format!("which has length {}, {}", actual_size, self.expected.explain_match(actual_size))
             .into()
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::len;
     use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::{
         BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
     };
     use std::fmt::Debug;
-    use std::marker::PhantomData;
 
     #[test]
     fn len_matcher_match_vec() -> Result<()> {
@@ -112,7 +104,7 @@
     #[test]
     fn len_matcher_match_array_reference() -> Result<()> {
         let value = &[1, 2, 3];
-        verify_that!(*value, len(eq(3)))
+        verify_that!(value, len(eq(3)))
     }
 
     #[test]
@@ -125,7 +117,7 @@
     fn len_matcher_match_slice_of_vec() -> Result<()> {
         let value = vec![1, 2, 3];
         let slice = value.as_slice();
-        verify_that!(*slice, len(eq(3)))
+        verify_that!(slice, len(eq(3)))
     }
 
     #[test]
@@ -178,11 +170,10 @@
 
     #[test]
     fn len_matcher_explain_match() -> Result<()> {
-        struct TestMatcher<T>(PhantomData<T>);
-        impl<T: Debug> Matcher for TestMatcher<T> {
-            type ActualT = T;
-
-            fn matches(&self, _: &T) -> MatcherResult {
+        #[derive(MatcherBase)]
+        struct TestMatcher;
+        impl<T: Debug + Copy> Matcher<T> for TestMatcher {
+            fn matches(&self, _: T) -> MatcherResult {
                 false.into()
             }
 
@@ -190,12 +181,12 @@
                 "called described".into()
             }
 
-            fn explain_match(&self, _: &T) -> Description {
+            fn explain_match(&self, _: T) -> Description {
                 "called explain_match".into()
             }
         }
         verify_that!(
-            len(TestMatcher(Default::default())).explain_match(&[1, 2, 3]),
+            len(TestMatcher).explain_match([1, 2, 3]),
             displays_as(eq("which has length 3, called explain_match"))
         )
     }
diff --git a/crates/googletest/src/matchers/lt_matcher.rs b/crates/googletest/src/matchers/lt_matcher.rs
index 08bc563..1758e83 100644
--- a/crates/googletest/src/matchers/lt_matcher.rs
+++ b/crates/googletest/src/matchers/lt_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a value less (in the sense of `<`) than `expected`.
 ///
@@ -74,24 +74,20 @@
 ///
 /// You can find the standard library `PartialOrd` implementation in
 /// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn lt<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
-    expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
-    LtMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn lt<ExpectedT>(expected: ExpectedT) -> LtMatcher<ExpectedT> {
+    LtMatcher { expected }
 }
 
-struct LtMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct LtMatcher<ExpectedT> {
     expected: ExpectedT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
-    for LtMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+    for LtMatcher<ExpectedT>
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
-        (*actual < self.expected).into()
+    fn matches(&self, actual: ActualT) -> MatcherResult {
+        (actual < self.expected).into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -106,8 +102,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::lt;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::ffi::OsString;
@@ -122,14 +117,14 @@
     #[test]
     fn lt_does_not_match_equal_i32() -> Result<()> {
         let matcher = lt(10);
-        let result = matcher.matches(&10);
+        let result = matcher.matches(10);
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
     fn lt_does_not_match_lower_i32() -> Result<()> {
         let matcher = lt(-50);
-        let result = matcher.matches(&50);
+        let result = matcher.matches(50);
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -141,7 +136,7 @@
     #[test]
     fn lt_does_not_match_bigger_str() -> Result<()> {
         let matcher = lt("ab");
-        let result = matcher.matches(&"az");
+        let result = matcher.matches("az");
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
@@ -195,7 +190,7 @@
         /// A custom "number" that is smaller than all other numbers. The only
         /// things we define about this "special" number is `PartialOrd` and
         /// `PartialEq` against `u32`.
-        #[derive(Debug)]
+        #[derive(Debug, Clone, Copy)]
         struct VeryLowNumber {}
 
         impl std::cmp::PartialEq<u32> for VeryLowNumber {
diff --git a/crates/googletest/src/matchers/matches_pattern.rs b/crates/googletest/src/matchers/matches_pattern.rs
index 106a5d7..7dc9ce3 100644
--- a/crates/googletest/src/matchers/matches_pattern.rs
+++ b/crates/googletest/src/matchers/matches_pattern.rs
@@ -84,7 +84,7 @@
 ///     a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
 /// };
 /// verify_that!(my_struct, matches_pattern!(MyStruct {
-///     a_nested_struct: matches_pattern!(MyInnerStruct {
+///      a_nested_struct: matches_pattern!(MyInnerStruct {
 ///         a_field: starts_with("Something"),
 ///     }),
 /// }))
@@ -138,12 +138,33 @@
 /// #     .unwrap();
 /// ```
 ///
+/// If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// struct MyStruct {
+///     a_field: String,
+///     another_field: String,
+/// }
+///
+/// let my_struct = MyStruct {
+///     a_field: "this".into(),
+///     another_field: "that".into()
+/// };
+/// verify_that!(my_struct, matches_pattern!(MyStruct {
+///     a_field: "this",
+///     another_field: "that",
+/// }))
+/// #     .unwrap();
+/// ```
+///
 /// **Important**: The method should be pure function with a deterministic
 /// output and no side effects. In particular, in the event of an assertion
 /// failure, it will be invoked a second time, with the assertion failure output
 /// reflecting the *second* invocation.
 ///
-/// These may also include extra parameters you pass in:
+/// These may also include extra litteral parameters you pass in:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -157,13 +178,14 @@
 /// }
 ///
 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
-/// verify_that!(my_struct, matches_pattern!(MyStruct {
-///     append_to_a_field("a suffix"): ends_with("a suffix"),
+/// verify_that!(my_struct, matches_pattern!(&MyStruct {
+///     append_to_a_field("a suffix"): ref ends_with("a suffix"),
 /// }))
 /// #     .unwrap();
 /// ```
 ///
-/// If the method returns a reference, precede it with a `*`:
+/// You can precede both field and property matchers with a `ref` to match the
+/// result by reference:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -173,12 +195,34 @@
 /// # }
 /// #
 /// impl MyStruct {
-///     fn get_a_field_ref(&self) -> &String { &self.a_field }
+///     fn get_a_field_ref(&self) -> String { self.a_field.clone() }
+/// }
+///
+/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
+/// verify_that!(my_struct, matches_pattern!(&MyStruct {
+///     get_a_field_ref(): ref starts_with("Something"),
+/// }))
+/// #    .unwrap();
+/// ```
+///
+/// Note that if the `actual` is of type `&ActualT` and the pattern type is
+/// `ActualT`, this is automatically performed. This behavior is similar to the
+/// reference binding mode in pattern matching.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # #[derive(Debug)]
+/// # struct MyStruct {
+/// #     a_field: String,
+/// # }
+/// #
+/// impl MyStruct {
+///     fn get_a_field_ref(&self) -> String { self.a_field.clone() }
 /// }
 ///
 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
 /// verify_that!(my_struct, matches_pattern!(MyStruct {
-///     *get_a_field_ref(): starts_with("Something"),
+///     get_a_field_ref(): starts_with("Something"),
 /// }))
 /// #    .unwrap();
 /// ```
@@ -194,7 +238,7 @@
 /// let my_struct = MyTupleStruct("Something".into(), "Some other thing".into());
 /// verify_that!(
 ///     my_struct,
-///     matches_pattern!(MyTupleStruct(eq("Something"), eq("Some other thing")))
+///     matches_pattern!(&MyTupleStruct(ref eq("Something"), ref eq("Some other thing")))
 /// )
 /// #    .unwrap();
 /// ```
@@ -210,42 +254,34 @@
 /// }
 ///
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(MyEnum::A(123), matches_pattern!(MyEnum::A(eq(123))))?; // Passes
+/// verify_that!(MyEnum::A(123), matches_pattern!(&MyEnum::A(eq(123))))?; // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
-/// verify_that!(MyEnum::B, matches_pattern!(MyEnum::A(eq(123))))?; // Fails - wrong enum variant
+/// verify_that!(MyEnum::B, matches_pattern!(&MyEnum::A(eq(123))))?; // Fails - wrong enum variant
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
 /// # should_fail().unwrap_err();
 /// ```
 ///
-/// This macro does not support plain (non-struct) tuples. Use the macro
-/// [`tuple`] for that purpose.
+/// This macro does not support plain (non-struct) tuples. But it should not be
+/// necessary as tuple of matchers are matchers of tuple. In other words, if
+/// `MatcherU: Matcher<U>` and `MatcherT: Matcher<T>`, then `(MatcherU,
+/// MatcherT): Matcher<(U, T)>`.
 ///
 /// Trailing commas are allowed (but not required) in both ordinary and tuple
 /// structs.
 ///
-/// Unfortunately, this matcher does *not* work with methods returning string
-/// slices:
+/// Note that the default format (rustfmt) can format macros if the macro
+/// argument is parseable Rust code. This is mostly true for this macro with two
+/// exceptions:
+///  * property matching
+///  * `ref` keyword with named fields
 ///
-/// ```compile_fail
-/// # use googletest::prelude::*;
-/// # #[derive(Debug)]
-/// pub struct MyStruct {
-///     a_string: String,
-/// }
-/// impl MyStruct {
-///     pub fn get_a_string(&self) -> &str { &self.a_string }
-/// }
-///
-/// let value = MyStruct { a_string: "A string".into() };
-/// verify_that!(value, matches_pattern!( MyStruct {
-///     get_a_string(): eq("A string"),   // Does not compile
-/// }))
-/// #    .unwrap();
-/// ```
+/// An option for formatting large is to avoid these exceptions (by removing the
+/// parenthesis of properties and the `ref` keywords), run `rustfmt` and add
+/// them back.
 #[macro_export]
 #[doc(hidden)]
 macro_rules! __matches_pattern {
@@ -258,7 +294,17 @@
 #[macro_export]
 macro_rules! matches_pattern_internal {
     (
-        [$($struct_name:tt)*],
+        @name [$($struct_name:tt)*],
+        { $field_name:ident : ref $matcher:expr $(,)? }
+    ) => {
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(field!($($struct_name)*.$field_name, ref $matcher))
+        )
+    };
+
+    (
+        @name [$($struct_name:tt)*],
         { $field_name:ident : $matcher:expr $(,)? }
     ) => {
         $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
@@ -268,7 +314,17 @@
     };
 
     (
-        [$($struct_name:tt)*],
+        @name [$($struct_name:tt)*],
+        { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr $(,)? }
+    ) => {
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(property!($($struct_name)*.$property_name($($argument),*), ref $matcher))
+        )
+    };
+
+    (
+        @name [$($struct_name:tt)*],
         { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
     ) => {
         $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
@@ -278,88 +334,123 @@
     };
 
     (
-        [$($struct_name:tt)*],
-        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
-    ) => {
-        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
-            stringify!($($struct_name)*),
-            all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher))
-        )
-    };
-
-    (
-        [$($struct_name:tt)*],
-        { $field_name:ident : $matcher:expr, $($rest:tt)* }
+        @name [$($struct_name:tt)*],
+        { $field_name:ident : ref $matcher:expr, $($rest:tt)* }
     ) => {
         $crate::matches_pattern_internal!(
-            all!(field!($($struct_name)*.$field_name, $matcher)),
+            @fields (field!($($struct_name)*.$field_name, ref $matcher)),
             [$($struct_name)*],
             { $($rest)* }
         )
     };
 
     (
-        [$($struct_name:tt)*],
+        @name [$($struct_name:tt)*],
+        { $field_name:ident : $matcher:expr, $($rest:tt)* }
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (field!($($struct_name)*.$field_name, $matcher)),
+            [$($struct_name)*],
+            { $($rest)* }
+        )
+    };
+
+    (
+        @name [$($struct_name:tt)*],
+        { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr, $($rest:tt)* }
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (property!($($struct_name)*.$property_name($($argument),*), ref $matcher)),
+            [$($struct_name)*],
+            { $($rest)* }
+        )
+    };
+
+    (
+        @name [$($struct_name:tt)*],
         { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
     ) => {
         $crate::matches_pattern_internal!(
-            all!(property!($($struct_name)*.$property_name($($argument),*), $matcher)),
+            @fields (property!($($struct_name)*.$property_name($($argument),*), $matcher)),
             [$($struct_name)*],
             { $($rest)* }
         )
     };
 
     (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
-        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
+        { $field_name:ident : ref $matcher:expr $(,)? }
     ) => {
-        $crate::matches_pattern_internal!(
-            all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher)),
-            [$($struct_name)*],
-            { $($rest)* }
-        )
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(
+                $($processed)*,
+                field!($($struct_name)*.$field_name, ref $matcher)
+            ))
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         { $field_name:ident : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
-            $($processed)*,
-            field!($($struct_name)*.$field_name, $matcher)
-        ))
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(
+                $($processed)*,
+                field!($($struct_name)*.$field_name, $matcher)
+            ))
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr $(,)? }
+    ) => {
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(
+                $($processed)*,
+                property!($($struct_name)*.$property_name($($argument),*), ref $matcher)
+            ))
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
-            $($processed)*,
-            property!($($struct_name)*.$property_name($($argument),*), $matcher)
-        ))
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(
+                $($processed)*,
+                property!($($struct_name)*.$property_name($($argument),*), $matcher)
+            ))
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
-        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
+        { $field_name:ident : ref $matcher:expr, $($rest:tt)* }
     ) => {
-        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
-            $($processed)*,
-            property!(* $($struct_name)*.$property_name($($argument),*), $matcher)
-        ))
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.$field_name, ref $matcher)
+            ),
+            [$($struct_name)*],
+            { $($rest)* }
+        )
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         { $field_name:ident : $matcher:expr, $($rest:tt)* }
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.$field_name, $matcher)
             ),
@@ -369,12 +460,27 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr, $($rest:tt)* }
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                property!(ref $($struct_name)*.$property_name($($argument),*), $matcher)
+            ),
+            [$($struct_name)*],
+            { $($rest)* }
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 property!($($struct_name)*.$property_name($($argument),*), $matcher)
             ),
@@ -384,32 +490,26 @@
     };
 
     (
-        all!($($processed:tt)*),
-        [$($struct_name:tt)*],
-        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
+        @name [$($struct_name:tt)*],
     ) => {
-        $crate::matches_pattern_internal!(
-            all!(
-                $($processed)*,
-                property!(* $($struct_name)*.$property_name($($argument),*), $matcher)
-            ),
-            [$($struct_name)*],
-            { $($rest)* }
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::pattern_only(
+            |v| matches!(v, $($struct_name)*),
+            concat!("is ", stringify!($($struct_name)*)),
+            concat!("is not ", stringify!($($struct_name)*)))
+    };
+
+    (
+        @name [$($struct_name:tt)*],
+        (ref $matcher:expr $(,)?)
+    ) => {
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(field!($($struct_name)*.0, ref $matcher))
         )
     };
 
     (
-        [$($struct_name:tt)*],
-    ) => {
-        $crate::matchers::predicate(|v| matches!(v, $($struct_name)*))
-            .with_description(
-                concat!("is ", stringify!($($struct_name)*)),
-                concat!("is not ", stringify!($($struct_name)*)),
-            )
-    };
-
-    (
-        [$($struct_name:tt)*],
+        @name [$($struct_name:tt)*],
         ($matcher:expr $(,)?)
     ) => {
         $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
@@ -419,11 +519,25 @@
     };
 
     (
-        [$($struct_name:tt)*],
+        @name [$($struct_name:tt)*],
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                field!($($struct_name)*.0, ref $matcher)
+            ),
+            [$($struct_name)*],
+            1,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @name [$($struct_name:tt)*],
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 field!($($struct_name)*.0, $matcher)
             ),
             [$($struct_name)*],
@@ -433,28 +547,61 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        $field:tt,
+        (ref $matcher:expr $(,)?)
+    ) => {
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(
+                $($processed)*,
+                field!($($struct_name)*.$field, ref $matcher)
+            ))
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         $field:tt,
         ($matcher:expr $(,)?)
     ) => {
-        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
-            $($processed)*,
-            field!($($struct_name)*.$field, $matcher)
-        ))
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+            stringify!($($struct_name)*),
+            all!(
+                $($processed)*,
+                field!($($struct_name)*.$field, $matcher)
+            ))
     };
 
     // We need to repeat this once for every supported field position, unfortunately. There appears
     // to be no way in declarative macros to compute $field + 1 and have the result evaluated to a
     // token which can be used as a tuple index.
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        1,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.1, ref $matcher)
+            ),
+            [$($struct_name)*],
+            2,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         1,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.1, $matcher)
             ),
@@ -465,13 +612,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        2,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.2, ref $matcher)
+            ),
+            [$($struct_name)*],
+            3,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         2,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.2, $matcher)
             ),
@@ -482,13 +646,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        3,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.3, ref $matcher)
+            ),
+            [$($struct_name)*],
+            4,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         3,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.3, $matcher)
             ),
@@ -499,13 +680,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        4,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.4, ref $matcher)
+            ),
+            [$($struct_name)*],
+            5,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         4,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.4, $matcher)
             ),
@@ -516,13 +714,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        5,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.5, ref $matcher)
+            ),
+            [$($struct_name)*],
+            6,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         5,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.5, $matcher)
             ),
@@ -533,13 +748,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        6,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.6, ref $matcher)
+            ),
+            [$($struct_name)*],
+            7,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         6,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.6, $matcher)
             ),
@@ -550,13 +782,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        7,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.7, ref $matcher)
+            ),
+            [$($struct_name)*],
+            8,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         7,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.7, $matcher)
             ),
@@ -567,13 +816,30 @@
     };
 
     (
-        all!($($processed:tt)*),
+        @fields ($($processed:tt)*),
+        [$($struct_name:tt)*],
+        8,
+        (ref $matcher:expr, $($rest:tt)*)
+    ) => {
+        $crate::matches_pattern_internal!(
+            @fields (
+                $($processed)*,
+                field!($($struct_name)*.8, ref $matcher)
+            ),
+            [$($struct_name)*],
+            9,
+            ($($rest)*)
+        )
+    };
+
+    (
+        @fields ($($processed:tt)*),
         [$($struct_name:tt)*],
         8,
         ($matcher:expr, $($rest:tt)*)
     ) => {
         $crate::matches_pattern_internal!(
-            all!(
+            @fields (
                 $($processed)*,
                 field!($($struct_name)*.8, $matcher)
             ),
@@ -583,14 +849,14 @@
         )
     };
 
-    ([$($struct_name:tt)*], $first:tt $($rest:tt)*) => {
-        $crate::matches_pattern_internal!([$($struct_name)* $first], $($rest)*)
+    (@name [$($struct_name:tt)*], $first:tt $($rest:tt)*) => {
+        $crate::matches_pattern_internal!(@name [$($struct_name)* $first], $($rest)*)
     };
 
     ($first:tt $($rest:tt)*) => {{
         #[allow(unused)]
         use $crate::matchers::{all, field, property};
-        $crate::matches_pattern_internal!([$first], $($rest)*)
+        $crate::matches_pattern_internal!(@name [$first], $($rest)*)
     }};
 }
 
@@ -600,3 +866,59 @@
 macro_rules! __pat {
     ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
 }
+
+#[doc(hidden)]
+pub mod internal {
+    use crate::matcher::{Matcher, MatcherBase};
+    use std::fmt::Debug;
+
+    // Specialized implementation of the `predicate` matcher to support ref binding
+    // mode for `matches_pattern`.
+    pub fn pattern_only<T>(
+        matcher_function: fn(&T) -> bool,
+        match_description: &'static str,
+        no_match_description: &'static str,
+    ) -> PatternOnlyMatcher<T> {
+        PatternOnlyMatcher { matcher_function, match_description, no_match_description }
+    }
+
+    #[derive(MatcherBase)]
+    #[doc(hidden)]
+    pub struct PatternOnlyMatcher<T> {
+        matcher_function: fn(&T) -> bool,
+        match_description: &'static str,
+        no_match_description: &'static str,
+    }
+
+    impl<'a, T: Debug> Matcher<&'a T> for PatternOnlyMatcher<T> {
+        fn matches(&self, actual: &'a T) -> crate::matcher::MatcherResult {
+            (self.matcher_function)(actual).into()
+        }
+
+        fn describe(
+            &self,
+            matcher_result: crate::matcher::MatcherResult,
+        ) -> crate::description::Description {
+            match matcher_result {
+                crate::matcher::MatcherResult::Match => self.match_description.into(),
+                crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
+            }
+        }
+    }
+
+    impl<T: Debug + Copy> Matcher<T> for PatternOnlyMatcher<T> {
+        fn matches(&self, actual: T) -> crate::matcher::MatcherResult {
+            (self.matcher_function)(&actual).into()
+        }
+
+        fn describe(
+            &self,
+            matcher_result: crate::matcher::MatcherResult,
+        ) -> crate::description::Description {
+            match matcher_result {
+                crate::matcher::MatcherResult::Match => self.match_description.into(),
+                crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
+            }
+        }
+    }
+}
diff --git a/crates/googletest/src/matchers/matches_regex_matcher.rs b/crates/googletest/src/matchers/matches_regex_matcher.rs
index 32b053b..2e00fb6 100644
--- a/crates/googletest/src/matchers/matches_regex_matcher.rs
+++ b/crates/googletest/src/matchers/matches_regex_matcher.rs
@@ -13,10 +13,9 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use regex::Regex;
 use std::fmt::Debug;
-use std::marker::PhantomData;
 use std::ops::Deref;
 
 /// Matches a string the entirety of which which matches the given regular
@@ -60,38 +59,31 @@
 // compiler treats it as a Matcher<str> only and the code
 //   verify_that!("Some value".to_string(), matches_regex(".*value"))?;
 // doesn't compile.
-pub fn matches_regex<ActualT: ?Sized, PatternT: Deref<Target = str>>(
+pub fn matches_regex<PatternT: Deref<Target = str>>(
     pattern: PatternT,
-) -> MatchesRegexMatcher<ActualT, PatternT> {
+) -> MatchesRegexMatcher<PatternT> {
     let adjusted_pattern = format!("^{}$", pattern.deref());
     let regex = Regex::new(adjusted_pattern.as_str()).unwrap();
-    MatchesRegexMatcher {
-        regex,
-        pattern,
-        _adjusted_pattern: adjusted_pattern,
-        phantom: Default::default(),
-    }
+    MatchesRegexMatcher { regex, pattern, _adjusted_pattern: adjusted_pattern }
 }
 
 /// A matcher matching a string-like type matching a given regular expression.
 ///
 /// Intended only to be used from the function [`matches_regex`] only.
 /// Should not be referenced by code outside this library.
-pub struct MatchesRegexMatcher<ActualT: ?Sized, PatternT: Deref<Target = str>> {
+#[derive(MatcherBase)]
+pub struct MatchesRegexMatcher<PatternT: Deref<Target = str>> {
     regex: Regex,
     pattern: PatternT,
     _adjusted_pattern: String,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<PatternT, ActualT> Matcher for MatchesRegexMatcher<ActualT, PatternT>
+impl<PatternT, ActualT> Matcher<ActualT> for MatchesRegexMatcher<PatternT>
 where
     PatternT: Deref<Target = str>,
-    ActualT: AsRef<str> + Debug + ?Sized,
+    ActualT: AsRef<str> + Debug + Copy,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         self.regex.is_match(actual.as_ref()).into()
     }
 
@@ -109,8 +101,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::{matches_regex, MatchesRegexMatcher};
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
 
     #[test]
@@ -201,10 +192,10 @@
 
     #[test]
     fn matches_regex_displays_quoted_debug_of_pattern() -> Result<()> {
-        let matcher: MatchesRegexMatcher<&str, _> = matches_regex("\n");
+        let matcher = matches_regex("\n");
 
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("matches the regular expression \"\\n\""))
         )
     }
diff --git a/crates/googletest/src/matchers/mod.rs b/crates/googletest/src/matchers/mod.rs
index 1e028b9..16ca900 100644
--- a/crates/googletest/src/matchers/mod.rs
+++ b/crates/googletest/src/matchers/mod.rs
@@ -17,17 +17,18 @@
 mod all_matcher;
 mod any_matcher;
 mod anything_matcher;
+mod bool_matcher;
 mod char_count_matcher;
 mod conjunction_matcher;
 mod container_eq_matcher;
 mod contains_matcher;
 mod contains_regex_matcher;
+mod derefs_to_matcher;
 mod disjunction_matcher;
 mod display_matcher;
 mod each_matcher;
 mod elements_are_matcher;
 mod empty_matcher;
-mod eq_deref_of_matcher;
 mod eq_matcher;
 mod err_matcher;
 mod field_matcher;
@@ -50,6 +51,7 @@
 mod pointwise_matcher;
 mod predicate_matcher;
 mod property_matcher;
+mod result_of_matcher;
 mod some_matcher;
 mod str_matcher;
 mod subset_of_matcher;
@@ -58,14 +60,15 @@
 mod unordered_elements_are_matcher;
 
 pub use anything_matcher::anything;
+pub use bool_matcher::{is_false, is_true};
 pub use char_count_matcher::char_count;
 pub use container_eq_matcher::container_eq;
 pub use contains_matcher::{contains, ContainsMatcher};
 pub use contains_regex_matcher::contains_regex;
+pub use derefs_to_matcher::derefs_to;
 pub use display_matcher::displays_as;
 pub use each_matcher::each;
 pub use empty_matcher::empty;
-pub use eq_deref_of_matcher::eq_deref_of;
 pub use eq_matcher::{eq, EqMatcher};
 pub use err_matcher::err;
 pub use ge_matcher::ge;
@@ -95,8 +98,8 @@
 pub use crate::{
     __all as all, __any as any, __contains_each as contains_each, __elements_are as elements_are,
     __field as field, __is_contained_in as is_contained_in, __matches_pattern as matches_pattern,
-    __pat as pat, __pointwise as pointwise, __property as property,
-    __unordered_elements_are as unordered_elements_are,
+    __pat as pat, __pointwise as pointwise, __property as property, __result_of as result_of,
+    __result_of_ref as result_of_ref, __unordered_elements_are as unordered_elements_are,
 };
 
 // Types and functions used by macros matchers.
@@ -105,16 +108,16 @@
 // should only be used through their respective macros.
 #[doc(hidden)]
 pub mod __internal_unstable_do_not_depend_on_these {
-    pub use super::all_matcher::internal::AllMatcher;
-    pub use super::any_matcher::internal::AnyMatcher;
     pub use super::conjunction_matcher::ConjunctionMatcher;
     pub use super::disjunction_matcher::DisjunctionMatcher;
     pub use super::elements_are_matcher::internal::ElementsAre;
     pub use super::field_matcher::internal::field_matcher;
     pub use super::is_matcher::is;
+    pub use super::matches_pattern::internal::pattern_only;
     pub use super::pointwise_matcher::internal::PointwiseMatcher;
     pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};
+    pub use super::result_of_matcher::internal::{result_of, result_of_ref};
     pub use super::unordered_elements_are_matcher::internal::{
-        Requirements, UnorderedElementsAreMatcher, UnorderedElementsOfMapAreMatcher,
+        Requirements, UnorderedElementsAreMatcher,
     };
 }
diff --git a/crates/googletest/src/matchers/near_matcher.rs b/crates/googletest/src/matchers/near_matcher.rs
index ca7cbdf..de1eb3d 100644
--- a/crates/googletest/src/matchers/near_matcher.rs
+++ b/crates/googletest/src/matchers/near_matcher.rs
@@ -14,7 +14,7 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
 use num_traits::{Float, FloatConst};
 use std::fmt::Debug;
@@ -110,6 +110,7 @@
 /// # }
 /// # should_pass().unwrap();
 /// ```
+#[track_caller]
 pub fn near<T: Debug + Float + Copy>(expected: T, max_abs_error: T) -> NearMatcher<T> {
     if max_abs_error.is_nan() {
         panic!("max_abs_error must not be NaN");
@@ -139,6 +140,7 @@
 
 /// A matcher which matches floating-point numbers approximately equal to its
 /// expected value.
+#[derive(MatcherBase)]
 pub struct NearMatcher<T: Debug> {
     expected: T,
     max_abs_error: T,
@@ -166,15 +168,13 @@
     }
 }
 
-impl<T: Debug + Float> Matcher for NearMatcher<T> {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+impl<T: Debug + Float + Copy> Matcher<T> for NearMatcher<T> {
+    fn matches(&self, actual: T) -> MatcherResult {
         if self.nans_are_equal && self.expected.is_nan() && actual.is_nan() {
             return MatcherResult::Match;
         }
 
-        let delta = *actual - self.expected;
+        let delta = actual - self.expected;
         if delta >= -self.max_abs_error && delta <= self.max_abs_error {
             MatcherResult::Match
         } else {
@@ -196,15 +196,14 @@
 
 #[cfg(test)]
 mod tests {
-    use super::{approx_eq, near};
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
 
     #[test]
     fn matches_value_inside_range() -> Result<()> {
         let matcher = near(1.0f64, 0.1f64);
 
-        let result = matcher.matches(&1.0f64);
+        let result = matcher.matches(1.0f64);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -213,7 +212,7 @@
     fn matches_value_at_low_end_of_range() -> Result<()> {
         let matcher = near(1.0f64, 0.1f64);
 
-        let result = matcher.matches(&0.9f64);
+        let result = matcher.matches(0.9f64);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -222,7 +221,7 @@
     fn matches_value_at_high_end_of_range() -> Result<()> {
         let matcher = near(1.0f64, 0.25f64);
 
-        let result = matcher.matches(&1.25f64);
+        let result = matcher.matches(1.25f64);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -231,7 +230,7 @@
     fn does_not_match_value_below_low_end_of_range() -> Result<()> {
         let matcher = near(1.0f64, 0.1f64);
 
-        let result = matcher.matches(&0.899999f64);
+        let result = matcher.matches(0.899999f64);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -240,7 +239,7 @@
     fn does_not_match_value_above_high_end_of_range() -> Result<()> {
         let matcher = near(1.0f64, 0.1f64);
 
-        let result = matcher.matches(&1.100001f64);
+        let result = matcher.matches(1.100001f64);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -249,7 +248,7 @@
     fn nan_is_not_near_a_number() -> Result<()> {
         let matcher = near(0.0f64, f64::MAX);
 
-        let result = matcher.matches(&f64::NAN);
+        let result = matcher.matches(f64::NAN);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -283,7 +282,7 @@
     fn inf_is_not_near_inf() -> Result<()> {
         let matcher = near(f64::INFINITY, f64::MAX);
 
-        let result = matcher.matches(&f64::INFINITY);
+        let result = matcher.matches(f64::INFINITY);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -292,7 +291,7 @@
     fn inf_is_not_near_a_number() -> Result<()> {
         let matcher = near(f64::INFINITY, f64::MAX);
 
-        let result = matcher.matches(&f64::MIN);
+        let result = matcher.matches(f64::MIN);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -301,7 +300,7 @@
     fn any_two_numbers_are_within_inf_of_each_other() -> Result<()> {
         let matcher = near(f64::MIN, f64::INFINITY);
 
-        let result = matcher.matches(&f64::MAX);
+        let result = matcher.matches(f64::MAX);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
diff --git a/crates/googletest/src/matchers/none_matcher.rs b/crates/googletest/src/matchers/none_matcher.rs
index af28932..b7a3555 100644
--- a/crates/googletest/src/matchers/none_matcher.rs
+++ b/crates/googletest/src/matchers/none_matcher.rs
@@ -13,9 +13,8 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use std::fmt::Debug;
-use std::marker::PhantomData;
 
 /// Matches an `Option` containing `None`.
 ///
@@ -32,19 +31,29 @@
 /// # should_pass().unwrap();
 /// # should_fail().unwrap_err();
 /// ```
-pub fn none<T: Debug>() -> impl Matcher<ActualT = Option<T>> {
-    NoneMatcher::<T> { phantom: Default::default() }
+pub fn none() -> NoneMatcher {
+    NoneMatcher
 }
 
-struct NoneMatcher<T> {
-    phantom: PhantomData<T>,
+#[derive(MatcherBase)]
+pub struct NoneMatcher;
+
+impl<T: Debug + Copy> Matcher<Option<T>> for NoneMatcher {
+    fn matches(&self, actual: Option<T>) -> MatcherResult {
+        actual.is_none().into()
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        match matcher_result {
+            MatcherResult::Match => "is none".into(),
+            MatcherResult::NoMatch => "is some(_)".into(),
+        }
+    }
 }
 
-impl<T: Debug> Matcher for NoneMatcher<T> {
-    type ActualT = Option<T>;
-
-    fn matches(&self, actual: &Option<T>) -> MatcherResult {
-        (actual.is_none()).into()
+impl<'a, T: Debug> Matcher<&'a Option<T>> for NoneMatcher {
+    fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
+        actual.is_none().into()
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -57,15 +66,14 @@
 
 #[cfg(test)]
 mod tests {
-    use super::none;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
 
     #[test]
     fn none_matches_option_with_none() -> Result<()> {
-        let matcher = none::<i32>();
+        let matcher = none();
 
-        let result = matcher.matches(&None);
+        let result = matcher.matches(None::<i32>);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -74,8 +82,47 @@
     fn none_does_not_match_option_with_value() -> Result<()> {
         let matcher = none();
 
-        let result = matcher.matches(&Some(0));
+        let result = matcher.matches(Some(0));
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
+
+    #[test]
+    fn none_matches_option_by_ref() -> Result<()> {
+        verify_that!(None::<String>, none())
+    }
+    #[test]
+    fn none_does_not_match_option_with_value_by_ref() -> Result<()> {
+        verify_that!(Some("123".to_string()), not(none()))
+    }
+
+    #[test]
+    fn none_describe_match_option_by_ref() -> Result<()> {
+        verify_that!(
+            Matcher::<&Option<String>>::describe(&none(), MatcherResult::Match),
+            displays_as(eq("is none"))
+        )
+    }
+    #[test]
+    fn none_describe_no_match_option_by_ref() -> Result<()> {
+        verify_that!(
+            Matcher::<&Option<String>>::describe(&none(), MatcherResult::NoMatch),
+            displays_as(eq("is some(_)"))
+        )
+    }
+
+    #[test]
+    fn none_describe_match_option() -> Result<()> {
+        verify_that!(
+            Matcher::<Option<i32>>::describe(&none(), MatcherResult::Match),
+            displays_as(eq("is none"))
+        )
+    }
+    #[test]
+    fn none_describe_no_match_option() -> Result<()> {
+        verify_that!(
+            Matcher::<Option<i32>>::describe(&none(), MatcherResult::NoMatch),
+            displays_as(eq("is some(_)"))
+        )
+    }
 }
diff --git a/crates/googletest/src/matchers/not_matcher.rs b/crates/googletest/src/matchers/not_matcher.rs
index f03d4ce..fa5f192 100644
--- a/crates/googletest/src/matchers/not_matcher.rs
+++ b/crates/googletest/src/matchers/not_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches the actual value exactly when the inner matcher does _not_ match.
 ///
@@ -33,28 +33,24 @@
 /// # should_pass().unwrap();
 /// # should_fail().unwrap_err();
 /// ```
-pub fn not<T: Debug, InnerMatcherT: Matcher<ActualT = T>>(
-    inner: InnerMatcherT,
-) -> impl Matcher<ActualT = T> {
-    NotMatcher::<T, _> { inner, phantom: Default::default() }
+pub fn not<InnerMatcherT>(inner: InnerMatcherT) -> NotMatcher<InnerMatcherT> {
+    NotMatcher { inner }
 }
 
-struct NotMatcher<T, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct NotMatcher<InnerMatcherT> {
     inner: InnerMatcherT,
-    phantom: PhantomData<T>,
 }
 
-impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher for NotMatcher<T, InnerMatcherT> {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<T> for NotMatcher<InnerMatcherT> {
+    fn matches(&self, actual: T) -> MatcherResult {
         match self.inner.matches(actual) {
             MatcherResult::Match => MatcherResult::NoMatch,
             MatcherResult::NoMatch => MatcherResult::Match,
         }
     }
 
-    fn explain_match(&self, actual: &T) -> Description {
+    fn explain_match(&self, actual: T) -> Description {
         self.inner.explain_match(actual)
     }
 
@@ -69,8 +65,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::not;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -78,7 +73,7 @@
     fn matches_when_inner_matcher_does_not_match() -> Result<()> {
         let matcher = not(eq(1));
 
-        let result = matcher.matches(&0);
+        let result = matcher.matches(0);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -87,14 +82,14 @@
     fn does_not_match_when_inner_matcher_matches() -> Result<()> {
         let matcher = not(eq(1));
 
-        let result = matcher.matches(&1);
+        let result = matcher.matches(1);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
     fn match_explanation_references_actual_value() -> Result<()> {
-        let result = verify_that!([1], not(container_eq([1])));
+        let result = verify_that!(&[1], not(container_eq([1])));
 
         verify_that!(
             result,
diff --git a/crates/googletest/src/matchers/ok_matcher.rs b/crates/googletest/src/matchers/ok_matcher.rs
index 8425b93..f399673 100644
--- a/crates/googletest/src/matchers/ok_matcher.rs
+++ b/crates/googletest/src/matchers/ok_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a `Result` containing `Ok` with a value matched by `inner`.
 ///
@@ -38,28 +38,55 @@
 /// # should_fail_1().unwrap_err();
 /// # should_fail_2().unwrap_err();
 /// ```
-pub fn ok<T: Debug, E: Debug>(
-    inner: impl Matcher<ActualT = T>,
-) -> impl Matcher<ActualT = std::result::Result<T, E>> {
-    OkMatcher::<T, E, _> { inner, phantom_t: Default::default(), phantom_e: Default::default() }
+pub fn ok<InnerMatcherT>(inner: InnerMatcherT) -> OkMatcher<InnerMatcherT> {
+    OkMatcher { inner }
 }
 
-struct OkMatcher<T, E, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct OkMatcher<InnerMatcherT> {
     inner: InnerMatcherT,
-    phantom_t: PhantomData<T>,
-    phantom_e: PhantomData<E>,
 }
 
-impl<T: Debug, E: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher
-    for OkMatcher<T, E, InnerMatcherT>
+impl<T: Debug + Copy, E: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<std::result::Result<T, E>>
+    for OkMatcher<InnerMatcherT>
 {
-    type ActualT = std::result::Result<T, E>;
+    fn matches(&self, actual: std::result::Result<T, E>) -> MatcherResult {
+        actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
+    }
 
-    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+    fn explain_match(&self, actual: std::result::Result<T, E>) -> Description {
+        match actual {
+            Ok(o) => {
+                Description::new().text("which is a success").nested(self.inner.explain_match(o))
+            }
+            Err(_) => "which is an error".into(),
+        }
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        match matcher_result {
+            MatcherResult::Match => format!(
+                "is a success containing a value, which {}",
+                self.inner.describe(MatcherResult::Match)
+            )
+            .into(),
+            MatcherResult::NoMatch => format!(
+                "is an error or a success containing a value, which {}",
+                self.inner.describe(MatcherResult::NoMatch)
+            )
+            .into(),
+        }
+    }
+}
+
+impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a std::result::Result<T, E>>
+    for OkMatcher<InnerMatcherT>
+{
+    fn matches(&self, actual: &'a std::result::Result<T, E>) -> MatcherResult {
         actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    fn explain_match(&self, actual: &'a std::result::Result<T, E>) -> Description {
         match actual {
             Ok(o) => {
                 Description::new().text("which is a success").nested(self.inner.explain_match(o))
@@ -86,8 +113,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::ok;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -96,7 +122,7 @@
         let matcher = ok(eq(1));
         let value: std::result::Result<i32, i32> = Ok(1);
 
-        let result = matcher.matches(&value);
+        let result = matcher.matches(value);
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -106,7 +132,7 @@
         let matcher = ok(eq(1));
         let value: std::result::Result<i32, i32> = Ok(0);
 
-        let result = matcher.matches(&value);
+        let result = matcher.matches(value);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
@@ -116,12 +142,30 @@
         let matcher = ok(eq(1));
         let value: std::result::Result<i32, i32> = Err(1);
 
-        let result = matcher.matches(&value);
+        let result = matcher.matches(value);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
+    fn ok_matches_result_with_value_by_ref() -> Result<()> {
+        let result: std::result::Result<String, String> = Ok("123".into());
+        verify_that!(result, ok(eq("123")))
+    }
+
+    #[test]
+    fn ok_does_not_match_result_with_wrong_value_by_ref() -> Result<()> {
+        let result: std::result::Result<String, String> = Ok("321".into());
+        verify_that!(result, not(ok(eq("123"))))
+    }
+
+    #[test]
+    fn ok_does_not_match_result_with_err_by_ref() -> Result<()> {
+        let result: std::result::Result<String, String> = Err("123".into());
+        verify_that!(result, not(ok(eq("123"))))
+    }
+
+    #[test]
     fn ok_full_error_message() -> Result<()> {
         let result = verify_that!(Ok::<i32, i32>(1), ok(eq(2)));
 
@@ -140,15 +184,107 @@
     }
 
     #[test]
-    fn ok_describe_matches() -> Result<()> {
-        let matcher = super::OkMatcher::<i32, i32, _> {
-            inner: eq(1),
-            phantom_t: Default::default(),
-            phantom_e: Default::default(),
-        };
+    fn ok_describe_match() -> Result<()> {
+        let matcher = ok(eq(1));
         verify_that!(
-            matcher.describe(MatcherResult::Match),
+            Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is a success containing a value, which is equal to 1"))
         )
     }
+
+    #[test]
+    fn ok_describe_no_match() -> Result<()> {
+        let matcher = ok(eq(1));
+        verify_that!(
+            Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::NoMatch),
+            displays_as(eq("is an error or a success containing a value, which isn't equal to 1"))
+        )
+    }
+
+    #[test]
+    fn ok_describe_match_by_ref() -> Result<()> {
+        let matcher = ok(eq(&1));
+        verify_that!(
+            Matcher::<&std::result::Result<i32, String>>::describe(&matcher, MatcherResult::Match),
+            displays_as(eq("is a success containing a value, which is equal to 1"))
+        )
+    }
+
+    #[test]
+    fn ok_describe_no_match_by_ref() -> Result<()> {
+        let matcher = ok(eq(&1));
+        verify_that!(
+            Matcher::<&std::result::Result<i32, String>>::describe(
+                &matcher,
+                MatcherResult::NoMatch
+            ),
+            displays_as(eq("is an error or a success containing a value, which isn't equal to 1"))
+        )
+    }
+
+    #[test]
+    fn ok_explain_match_ok_success() -> Result<()> {
+        let actual = Ok(1);
+        let matcher = ok(eq(1));
+
+        verify_that!(
+            Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
+            displays_as(eq("which is a success\n  which is equal to 1"))
+        )
+    }
+
+    #[test]
+    fn ok_explain_match_ok_fail() -> Result<()> {
+        let actual = Ok(1);
+        let matcher = ok(eq(2));
+
+        verify_that!(
+            Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
+            displays_as(eq("which is a success\n  which isn't equal to 2"))
+        )
+    }
+
+    #[test]
+    fn ok_explain_match_ok_err() -> Result<()> {
+        let actual = Err(1);
+        let matcher = ok(eq(2));
+
+        verify_that!(
+            Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
+            displays_as(eq("which is an error"))
+        )
+    }
+
+    #[test]
+    fn ok_explain_match_ok_success_by_ref() -> Result<()> {
+        let actual = Ok("123".to_string());
+        let matcher = ok(eq("123"));
+
+        verify_that!(
+            Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
+            displays_as(eq("which is a success\n  which is equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn ok_explain_match_ok_fail_by_ref() -> Result<()> {
+        let actual = Ok("321".to_string());
+        let matcher = ok(eq("123"));
+
+        verify_that!(
+            Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
+            displays_as(eq("which is a success\n  which isn't equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn ok_explain_match_ok_err_by_ref() -> Result<()> {
+        let actual = Err("123".to_string());
+        let matcher = ok(eq("123"));
+
+        verify_that!(
+            Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
+            displays_as(eq("which is an error"))
+        )
+    }
 }
diff --git a/crates/googletest/src/matchers/points_to_matcher.rs b/crates/googletest/src/matchers/points_to_matcher.rs
index 2c516d0..0653e03 100644
--- a/crates/googletest/src/matchers/points_to_matcher.rs
+++ b/crates/googletest/src/matchers/points_to_matcher.rs
@@ -13,55 +13,46 @@
 // limitations under the License.
 
 use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
 use std::fmt::Debug;
-use std::marker::PhantomData;
-use std::ops::Deref;
 
-/// Matches a (smart) pointer pointing to a value matched by the [`Matcher`]
+/// Matches a reference pointing to a value matched by the [`Matcher`]
 /// `expected`.
 ///
-/// This allows easily matching smart pointers such as `Box`, `Rc`, and `Arc`.
+/// This is useful for combining matchers, especially when working with
+/// iterators.
+///
 /// For example:
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(Box::new(123), points_to(eq(123)))?;
+/// verify_that!(&123, points_to(eq(123)))?;
+/// verify_that!(vec![1,2,3], each(points_to(gt(0))))?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
 /// ```
-pub fn points_to<ExpectedT, MatcherT, ActualT>(
-    expected: MatcherT,
-) -> impl Matcher<ActualT = ActualT>
-where
-    ExpectedT: Debug,
-    MatcherT: Matcher<ActualT = ExpectedT>,
-    ActualT: Deref<Target = ExpectedT> + Debug + ?Sized,
-{
-    PointsToMatcher { expected, phantom: Default::default() }
+pub fn points_to<MatcherT>(expected: MatcherT) -> PointsToMatcher<MatcherT> {
+    PointsToMatcher { expected }
 }
 
-struct PointsToMatcher<ActualT: ?Sized, MatcherT> {
+#[derive(MatcherBase)]
+pub struct PointsToMatcher<MatcherT> {
     expected: MatcherT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ExpectedT, MatcherT, ActualT> Matcher for PointsToMatcher<ActualT, MatcherT>
+impl<'a, ExpectedT, MatcherT> Matcher<&'a ExpectedT> for PointsToMatcher<MatcherT>
 where
-    ExpectedT: Debug,
-    MatcherT: Matcher<ActualT = ExpectedT>,
-    ActualT: Deref<Target = ExpectedT> + Debug + ?Sized,
+    ExpectedT: Debug + Copy,
+    MatcherT: Matcher<ExpectedT>,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
-        self.expected.matches(actual.deref())
+    fn matches(&self, actual: &'a ExpectedT) -> MatcherResult {
+        self.expected.matches(*actual)
     }
 
-    fn explain_match(&self, actual: &ActualT) -> Description {
-        self.expected.explain_match(actual.deref())
+    fn explain_match(&self, actual: &'a ExpectedT) -> Description {
+        self.expected.explain_match(*actual)
     }
 
     fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -71,36 +62,24 @@
 
 #[cfg(test)]
 mod tests {
-    use super::points_to;
     use crate::prelude::*;
     use indoc::indoc;
-    use std::rc::Rc;
 
     #[test]
-    fn points_to_matches_box_of_int_with_int() -> Result<()> {
-        verify_that!(Box::new(123), points_to(eq(123)))
-    }
-
-    #[test]
-    fn points_to_matches_rc_of_int_with_int() -> Result<()> {
-        verify_that!(Rc::new(123), points_to(eq(123)))
-    }
-
-    #[test]
-    fn points_to_matches_box_of_owned_string_with_string_reference() -> Result<()> {
-        verify_that!(Rc::new("A string".to_string()), points_to(eq("A string")))
+    fn points_to_matches_ref() -> Result<()> {
+        verify_that!(&123, points_to(eq(123)))
     }
 
     #[test]
     fn match_explanation_references_actual_value() -> Result<()> {
-        let result = verify_that!(&vec![1], points_to(container_eq([])));
+        let result = verify_that!(&1, points_to(eq(0)));
 
         verify_that!(
             result,
             err(displays_as(contains_substring(indoc!(
                 "
-                    Actual: [1],
-                      which contains the unexpected element 1
+                    Actual: 1,
+                      which isn't equal to 0
                 "
             ))))
         )
diff --git a/crates/googletest/src/matchers/pointwise_matcher.rs b/crates/googletest/src/matchers/pointwise_matcher.rs
index 01e70c0..a3a0d74 100644
--- a/crates/googletest/src/matchers/pointwise_matcher.rs
+++ b/crates/googletest/src/matchers/pointwise_matcher.rs
@@ -27,13 +27,14 @@
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// let value = vec![1, 2, 3];
-/// verify_that!(value, pointwise!(le, [1, 3, 3]))?; // Passes
-/// verify_that!(value, pointwise!(le, vec![1, 3, 3]))?; // Passes
+/// verify_that!(value, pointwise!(le, [&1, &2, &3]))?; // Passes
+/// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 2, 3]))?; // Passes
+/// verify_that!(value, pointwise!(|e| points_to(le(e)), vec![1, 3, 3]))?; // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
 /// #     let value = vec![1, 2, 3];
-/// verify_that!(value, pointwise!(le, [1, 1, 3]))?; // Fails
+/// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 1, 3]))?; // Fails
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -46,7 +47,7 @@
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// let value = vec![1.00001, 2.000001, 3.00001];
-/// verify_that!(value, pointwise!(|v| near(v, 0.001), [1.0, 2.0, 3.0]))?;
+/// verify_that!(value, pointwise!(|v| points_to(near(v, 0.001)), [1.0, 2.0, 3.0]))?;
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -59,12 +60,11 @@
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// let value = vec![1.00001, 2.000001, 3.00001];
-/// verify_that!(value, pointwise!(|v, t| near(v, t), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
-/// verify_that!(value, pointwise!(near, [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?; // Same as above
+/// verify_that!(value, pointwise!(|v, t| points_to(near(v, t)), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
 /// verify_that!(
 ///     value,
 ///     pointwise!(
-///         |v, t, u| near(v, t * u),
+///         |v, t, u| points_to(near(v, t * u)),
 ///         [1.0, 2.0, 3.0],
 ///         [0.001, 0.0001, 0.01],
 ///         [0.5, 0.5, 1.0]
@@ -79,25 +79,20 @@
 /// that all of the containers have the same size. This matcher does not check
 /// whether the sizes match.
 ///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// slice. More precisely, the actual value must implement [`IntoIterator`].
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// let value = vec![1, 2, 3];
-/// verify_that!(*value.as_slice(), pointwise!(le, [1, 3, 3]))?; // Passes
+/// verify_that!(value, pointwise!(|i| points_to(le(i)), [1, 3, 3]))?; // Passes
 /// verify_that!([1, 2, 3], pointwise!(le, [1, 3, 3]))?; // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
 /// ```
 ///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`]
-/// first.
-///
 /// The second argument can be any value implementing `IntoIterator`, such as a
 /// `Vec` or an array. The container does not have to have the same type as the
 /// actual value, but the value type must be the same.
@@ -119,13 +114,13 @@
 #[doc(hidden)]
 macro_rules! __pointwise {
     ($matcher:expr, $container:expr) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
-        PointwiseMatcher::new($container.into_iter().map($matcher).collect())
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
+            $container.into_iter().map($matcher).collect(),
+        )
     }};
 
     ($matcher:expr, $left_container:expr, $right_container:expr) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
-        PointwiseMatcher::new(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
             $left_container
                 .into_iter()
                 .zip($right_container.into_iter())
@@ -135,8 +130,7 @@
     }};
 
     ($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
-        PointwiseMatcher::new(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
             $left_container
                 .into_iter()
                 .zip($right_container.into_iter().zip($middle_container.into_iter()))
@@ -152,33 +146,31 @@
 #[doc(hidden)]
 pub mod internal {
     use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::{Matcher, MatcherBase, MatcherResult};
     use crate::matcher_support::zipped_iterator::zip;
-    use std::{fmt::Debug, marker::PhantomData};
+    use std::fmt::Debug;
 
     /// This struct is meant to be used only through the `pointwise` macro.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
-    pub struct PointwiseMatcher<ContainerT: ?Sized, MatcherT> {
+    #[derive(MatcherBase)]
+    pub struct PointwiseMatcher<MatcherT> {
         matchers: Vec<MatcherT>,
-        phantom: PhantomData<ContainerT>,
     }
 
-    impl<ContainerT: ?Sized, MatcherT> PointwiseMatcher<ContainerT, MatcherT> {
+    impl<MatcherT> PointwiseMatcher<MatcherT> {
         pub fn new(matchers: Vec<MatcherT>) -> Self {
-            Self { matchers, phantom: Default::default() }
+            Self { matchers }
         }
     }
 
-    impl<T: Debug, MatcherT: Matcher<ActualT = T>, ContainerT: ?Sized + Debug> Matcher
-        for PointwiseMatcher<ContainerT, MatcherT>
+    impl<T: Debug + Copy, MatcherT: Matcher<T>, ContainerT: Copy + Debug> Matcher<ContainerT>
+        for PointwiseMatcher<MatcherT>
     where
-        for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
+        ContainerT: IntoIterator<Item = T>,
     {
-        type ActualT = ContainerT;
-
-        fn matches(&self, actual: &ContainerT) -> MatcherResult {
+        fn matches(&self, actual: ContainerT) -> MatcherResult {
             let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
             for (element, matcher) in zipped_iterator.by_ref() {
                 if matcher.matches(element).is_no_match() {
@@ -192,7 +184,7 @@
             }
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> Description {
+        fn explain_match(&self, actual: ContainerT) -> Description {
             // TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider
             // extract as a separate library. (or implement pointwise! with
             // elements_are)
diff --git a/crates/googletest/src/matchers/predicate_matcher.rs b/crates/googletest/src/matchers/predicate_matcher.rs
index 5bc067d..1ba81c4 100644
--- a/crates/googletest/src/matchers/predicate_matcher.rs
+++ b/crates/googletest/src/matchers/predicate_matcher.rs
@@ -14,43 +14,37 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Creates a matcher based on the predicate provided.
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(3, predicate(|x: &i32| x % 2 == 1))?;  // Passes
+/// verify_that!(3, predicate(|x: i32| x % 2 == 1))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
 /// ```
 ///
-/// The predicate should take the subject type by reference and return a
+/// The predicate should take the subject type and return a
 /// boolean.
 ///
 /// Note: even if the Rust compiler should be able to infer the type of
 /// the closure argument, it is likely that it won't.
 /// See <https://github.com/rust-lang/rust/issues/12679> for update on this issue.
 /// This is easily fixed by explicitly declaring the type of the argument
-pub fn predicate<T: Debug + ?Sized, P>(
-    predicate: P,
-) -> PredicateMatcher<T, P, NoDescription, NoDescription>
-where
-    for<'a> P: Fn(&'a T) -> bool,
-{
+pub fn predicate<P>(predicate: P) -> PredicateMatcher<P, NoDescription, NoDescription> {
     PredicateMatcher {
         predicate,
         positive_description: NoDescription,
         negative_description: NoDescription,
-        phantom: Default::default(),
     }
 }
 
-impl<T, P> PredicateMatcher<T, P, NoDescription, NoDescription> {
+impl<P> PredicateMatcher<P, NoDescription, NoDescription> {
     /// Configures this instance to provide a more meaningful description.
     ///
     /// For example, to make sure the error message is more useful
@@ -58,7 +52,7 @@
     /// ```
     /// # use googletest::matchers::{predicate, PredicateMatcher};
     /// # let _ =
-    /// predicate(|x: &i32| x % 2 == 1)
+    /// predicate(|x: i32| x % 2 == 1)
     ///     .with_description("is odd", "is even")
     /// # ;
     /// ```
@@ -70,24 +64,19 @@
         self,
         positive_description: D1,
         negative_description: D2,
-    ) -> PredicateMatcher<T, P, D1, D2> {
-        PredicateMatcher {
-            predicate: self.predicate,
-            positive_description,
-            negative_description,
-            phantom: Default::default(),
-        }
+    ) -> PredicateMatcher<P, D1, D2> {
+        PredicateMatcher { predicate: self.predicate, positive_description, negative_description }
     }
 }
 
 /// A matcher which applies `predicate` on the value.
 ///
 /// See [`predicate`].
-pub struct PredicateMatcher<T: ?Sized, P, D1, D2> {
+#[derive(MatcherBase)]
+pub struct PredicateMatcher<P, D1, D2> {
     predicate: P,
     positive_description: D1,
     negative_description: D2,
-    phantom: PhantomData<T>,
 }
 
 /// A trait to allow [`PredicateMatcher::with_description`] to accept multiple
@@ -124,13 +113,11 @@
 #[doc(hidden)]
 pub struct NoDescription;
 
-impl<T: Debug, P> Matcher for PredicateMatcher<T, P, NoDescription, NoDescription>
+impl<T: Debug + Copy, P> Matcher<T> for PredicateMatcher<P, NoDescription, NoDescription>
 where
-    for<'a> P: Fn(&'a T) -> bool,
+    P: Fn(T) -> bool,
 {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+    fn matches(&self, actual: T) -> MatcherResult {
         (self.predicate)(actual).into()
     }
 
@@ -142,14 +129,12 @@
     }
 }
 
-impl<T: Debug, P, D1: PredicateDescription, D2: PredicateDescription> Matcher
-    for PredicateMatcher<T, P, D1, D2>
+impl<T: Debug + Copy, P, D1: PredicateDescription, D2: PredicateDescription> Matcher<T>
+    for PredicateMatcher<P, D1, D2>
 where
-    for<'a> P: Fn(&'a T) -> bool,
+    P: Fn(T) -> bool,
 {
-    type ActualT = T;
-
-    fn matches(&self, actual: &T) -> MatcherResult {
+    fn matches(&self, actual: T) -> MatcherResult {
         (self.predicate)(actual).into()
     }
 
@@ -163,12 +148,10 @@
 
 #[cfg(test)]
 mod tests {
-    use super::predicate;
-    use crate::matcher::Matcher;
     use crate::prelude::*;
 
     // Simple matcher with a description
-    fn is_odd() -> impl Matcher<ActualT = i32> {
+    fn is_odd() -> impl Matcher<i32> {
         predicate(|x| x % 2 == 1).with_description("is odd", "is even")
     }
 
@@ -179,16 +162,16 @@
 
     #[test]
     fn predicate_matcher_odd_explain_match_matches() -> Result<()> {
-        verify_that!(is_odd().explain_match(&1), displays_as(eq("which is odd")))
+        verify_that!(is_odd().explain_match(1), displays_as(eq("which is odd")))
     }
 
     #[test]
     fn predicate_matcher_odd_explain_match_does_not_match() -> Result<()> {
-        verify_that!(is_odd().explain_match(&2), displays_as(eq("which is even")))
+        verify_that!(is_odd().explain_match(2), displays_as(eq("which is even")))
     }
 
     // Simple Matcher without description
-    fn is_even() -> impl Matcher<ActualT = i32> {
+    fn is_even() -> impl Matcher<i32> {
         predicate(|x| x % 2 == 0)
     }
 
@@ -199,18 +182,18 @@
 
     #[test]
     fn predicate_matcher_even_explain_match_matches() -> Result<()> {
-        verify_that!(is_even().explain_match(&2), displays_as(eq("which matches")))
+        verify_that!(is_even().explain_match(2), displays_as(eq("which matches")))
     }
 
     #[test]
     fn predicate_matcher_even_explain_match_does_not_match() -> Result<()> {
-        verify_that!(is_even().explain_match(&1), displays_as(eq("which does not match")))
+        verify_that!(is_even().explain_match(1), displays_as(eq("which does not match")))
     }
 
     #[test]
     fn predicate_matcher_generator_lambda() -> Result<()> {
         let is_divisible_by = |quotient| {
-            predicate(move |x: &i32| x % quotient == 0).with_description(
+            predicate(move |x: i32| x % quotient == 0).with_description(
                 move || format!("is divisible by {quotient}"),
                 move || format!("is not divisible by {quotient}"),
             )
@@ -220,12 +203,12 @@
 
     #[test]
     fn predicate_matcher_inline() -> Result<()> {
-        verify_that!(2048, predicate(|x: &i32| x.count_ones() == 1))
+        verify_that!(2048, predicate(|x: i32| x.count_ones() == 1))
     }
 
     #[test]
     fn predicate_matcher_function_pointer() -> Result<()> {
         use std::time::Duration;
-        verify_that!(Duration::new(0, 0), predicate(Duration::is_zero))
+        verify_that!(&Duration::new(0, 0), predicate(Duration::is_zero))
     }
 }
diff --git a/crates/googletest/src/matchers/property_matcher.rs b/crates/googletest/src/matchers/property_matcher.rs
index 19b4862..98c51a6 100644
--- a/crates/googletest/src/matchers/property_matcher.rs
+++ b/crates/googletest/src/matchers/property_matcher.rs
@@ -35,7 +35,26 @@
 /// }
 ///
 /// let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(MyStruct.get_a_field(), eq(100))))
+/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), eq(100))))
+/// #    .unwrap();
+/// ```
+///
+///
+/// If the inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct MyStruct {
+///     a_field: u32,
+/// }
+///
+/// impl MyStruct {
+///     pub fn get_a_field(&self) -> u32 { self.a_field }
+/// }
+///
+/// let value = vec![MyStruct { a_field: 100 }];
+/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), 100)))
 /// #    .unwrap();
 /// ```
 ///
@@ -44,24 +63,7 @@
 /// failure, it will be invoked a second time, with the assertion failure output
 /// reflecting the *second* invocation.
 ///
-/// If the method returns a *reference*, then it must be preceded by a `*`:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # #[derive(Debug)]
-/// # pub struct MyStruct {
-/// #     a_field: u32,
-/// # }
-/// impl MyStruct {
-///     pub fn get_a_field(&self) -> &u32 { &self.a_field }
-/// }
-///
-/// # let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(*MyStruct.get_a_field(), eq(100))))
-/// #    .unwrap();
-/// ```
-///
-/// The method may also take additional arguments:
+/// The method may also take additional litteral arguments:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -74,26 +76,88 @@
 /// }
 ///
 /// # let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(MyStruct.add_to_a_field(50), eq(150))))
+/// verify_that!(value, contains(property!(&MyStruct.add_to_a_field(50), eq(150))))
 /// #    .unwrap();
 /// ```
 ///
-/// Unfortunately, this matcher does *not* work with methods returning string
-/// slices:
+/// The arguments must be litteral as `property!` is not able to capture them.
 ///
-/// ```compile_fail
+/// # Specification of the property pattern
+///
+/// The specification of the field follow the syntax: `(ref)? (&)?
+/// $TYPE.$PROPERTY\($ARGUMENT\)`.
+///
+/// The `&` allows to specify whether this matcher matches against an actual of
+/// type `$TYPE` (`$TYPE` must implement `Copy`) or a `&$TYPE`.
+///
+/// For instance:
+///
+/// ```
 /// # use googletest::prelude::*;
 /// #[derive(Debug)]
-/// pub struct MyStruct {
-///     a_string: String,
-/// }
-/// impl MyStruct {
-///     pub fn get_a_string(&self) -> &str { &self.a_string }
-/// }
+/// pub struct AStruct;
 ///
-/// let value = MyStruct { a_string: "A string".into() };
-/// verify_that!(value, property!(*MyStruct.get_a_string(), eq("A string"))) // Does not compile
-/// #    .unwrap();
+/// impl AStruct {
+///   fn a_property(&self) -> i32 {32}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug, Clone, Copy)]
+/// pub struct AStruct;
+///
+/// impl AStruct {
+///   fn a_property(self) -> i32 {32}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(AStruct.a_property(), eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// The `ref` allows to bind the property returned value by reference, which is
+/// required if the field type does not implement `Copy`.
+///
+/// For instance:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct;
+///
+/// impl AStruct {
+///   fn a_property(&self) -> i32 {32}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// If `property!` is qualified by both `&` and `ref`, they can both be omitted.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct;
+///
+/// impl AStruct {
+///   fn a_property(&self) -> String {"32".into()}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(&AStruct.a_property(), ref eq("32")))?;
+/// verify_that!(AStruct, property!(AStruct.a_property(), eq("32")))?;
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
 /// ```
 ///
 /// This macro is analogous to [`field`][crate::matchers::field], except that it
@@ -112,20 +176,30 @@
 #[doc(hidden)]
 #[macro_export]
 macro_rules! property_internal {
-    ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
-         use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher;
-        property_matcher(
-            |o: &$($t)::+| o.$method($($argument),*),
-            &stringify!($method($($argument),*)),
-            $m)
-    }};
 
-    (* $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher;
-        property_ref_matcher(
+    (&$($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
+            |o: &$($t)::+| $($t)::+::$method(o, $($argument),*),
+            &stringify!($method($($argument),*)),
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+    }};
+    ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
+            |o: $($t)::+| $($t)::+::$method(o, $($argument),*),
+            &stringify!($method($($argument),*)),
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+    }};
+    (& $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
+            |o: &&$($t)::+| o.$method($($argument),*),
+            &stringify!($method($($argument),*)),
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+    }};
+    ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
             |o: &$($t)::+| o.$method($($argument),*),
             &stringify!($method($($argument),*)),
-            $m)
+            $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
     }};
 }
 
@@ -136,37 +210,65 @@
 pub mod internal {
     use crate::{
         description::Description,
-        matcher::{Matcher, MatcherResult},
+        matcher::{Matcher, MatcherBase, MatcherResult},
     };
-    use std::{fmt::Debug, marker::PhantomData};
+    use std::fmt::Debug;
 
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
-    pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT: Matcher<ActualT = InnerT>>(
-        extractor: impl Fn(&OuterT) -> InnerT,
+    pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>(
+        extractor: fn(&OuterT) -> InnerT,
         property_desc: &'static str,
         inner: MatcherT,
-    ) -> impl Matcher<ActualT = OuterT> {
-        PropertyMatcher { extractor, property_desc, inner, phantom: Default::default() }
+    ) -> PropertyMatcher<OuterT, InnerT, MatcherT> {
+        PropertyMatcher { extractor, property_desc, inner }
     }
 
-    struct PropertyMatcher<OuterT, ExtractorT, MatcherT> {
-        extractor: ExtractorT,
+    #[derive(MatcherBase)]
+    pub struct PropertyMatcher<OuterT, InnerT, MatcherT> {
+        extractor: fn(&OuterT) -> InnerT,
         property_desc: &'static str,
         inner: MatcherT,
-        phantom: PhantomData<OuterT>,
     }
 
-    impl<InnerT, OuterT, ExtractorT, MatcherT> Matcher for PropertyMatcher<OuterT, ExtractorT, MatcherT>
+    impl<InnerT, OuterT, MatcherT> Matcher<OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
+    where
+        InnerT: Debug + Copy,
+        OuterT: Debug + Copy,
+        MatcherT: Matcher<InnerT>,
+    {
+        fn matches(&self, actual: OuterT) -> MatcherResult {
+            self.inner.matches((self.extractor)(&actual))
+        }
+
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
+            format!(
+                "has property `{}`, which {}",
+                self.property_desc,
+                self.inner.describe(matcher_result)
+            )
+            .into()
+        }
+
+        fn explain_match(&self, actual: OuterT) -> Description {
+            let actual_inner = (self.extractor)(&actual);
+            format!(
+                "whose property `{}` is `{:#?}`, {}",
+                self.property_desc,
+                actual_inner,
+                self.inner.explain_match(actual_inner)
+            )
+            .into()
+        }
+    }
+
+    impl<'a, InnerT, OuterT, MatcherT> Matcher<&'a OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
     where
         InnerT: Debug,
         OuterT: Debug,
-        ExtractorT: Fn(&OuterT) -> InnerT,
-        MatcherT: Matcher<ActualT = InnerT>,
+        MatcherT: for<'b> Matcher<&'b InnerT>,
     {
-        type ActualT = OuterT;
-
-        fn matches(&self, actual: &OuterT) -> MatcherResult {
+        fn matches(&self, actual: &'a OuterT) -> MatcherResult {
             self.inner.matches(&(self.extractor)(actual))
         }
 
@@ -179,7 +281,7 @@
             .into()
         }
 
-        fn explain_match(&self, actual: &OuterT) -> Description {
+        fn explain_match(&self, actual: &'a OuterT) -> Description {
             let actual_inner = (self.extractor)(actual);
             format!(
                 "whose property `{}` is `{:#?}`, {}",
@@ -193,32 +295,36 @@
 
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
-    pub fn property_ref_matcher<OuterT, InnerT, MatcherT>(
-        extractor: fn(&OuterT) -> &InnerT,
+    pub fn property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>(
+        extractor: ExtractorT,
         property_desc: &'static str,
         inner: MatcherT,
-    ) -> impl Matcher<ActualT = OuterT>
+    ) -> PropertyRefMatcher<ExtractorT, MatcherT>
     where
         OuterT: Debug,
-        InnerT: Debug + ?Sized,
-        MatcherT: Matcher<ActualT = InnerT>,
+        InnerT: Debug,
+        MatcherT: for<'a> Matcher<&'a InnerT>,
+        ExtractorT: Fn(OuterT) -> InnerT,
     {
         PropertyRefMatcher { extractor, property_desc, inner }
     }
 
-    struct PropertyRefMatcher<InnerT: ?Sized, OuterT, MatcherT> {
-        extractor: fn(&OuterT) -> &InnerT,
+    #[derive(MatcherBase)]
+    pub struct PropertyRefMatcher<ExtractorT, MatcherT> {
+        extractor: ExtractorT,
         property_desc: &'static str,
         inner: MatcherT,
     }
 
-    impl<InnerT: Debug + ?Sized, OuterT: Debug, MatcherT: Matcher<ActualT = InnerT>> Matcher
-        for PropertyRefMatcher<InnerT, OuterT, MatcherT>
+    impl<
+            InnerT: Debug,
+            OuterT: Debug + Copy,
+            MatcherT: for<'a> Matcher<&'a InnerT>,
+            ExtractorT: Fn(OuterT) -> InnerT,
+        > Matcher<OuterT> for PropertyRefMatcher<ExtractorT, MatcherT>
     {
-        type ActualT = OuterT;
-
-        fn matches(&self, actual: &OuterT) -> MatcherResult {
-            self.inner.matches((self.extractor)(actual))
+        fn matches(&self, actual: OuterT) -> MatcherResult {
+            self.inner.matches(&(self.extractor)(actual))
         }
 
         fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -230,13 +336,13 @@
             .into()
         }
 
-        fn explain_match(&self, actual: &OuterT) -> Description {
+        fn explain_match(&self, actual: OuterT) -> Description {
             let actual_inner = (self.extractor)(actual);
             format!(
                 "whose property `{}` is `{:#?}`, {}",
                 self.property_desc,
                 actual_inner,
-                self.inner.explain_match(actual_inner)
+                self.inner.explain_match(&actual_inner)
             )
             .into()
         }
diff --git a/crates/googletest/src/matchers/result_of_matcher.rs b/crates/googletest/src/matchers/result_of_matcher.rs
new file mode 100644
index 0000000..c75fe66
--- /dev/null
+++ b/crates/googletest/src/matchers/result_of_matcher.rs
@@ -0,0 +1,440 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+/// Matches a value where the result of `callable` applied to the value matches
+/// the inner matcher.
+///
+/// The `callable` will be called twice, so make sure it is pure.
+/// ```
+/// use googletest::prelude::*;
+/// fn should_pass() -> googletest::Result<()> {
+///    verify_that!(100, result_of!(|value| value + 1, eq(101)))?; // Passes
+///    Ok(())
+/// }
+///
+/// fn should_fail() -> googletest::Result<()> {
+///    verify_that!(100, result_of!(|value| value * 2, eq(100)))?; // Fails
+///    Ok(())
+/// }
+/// should_pass().unwrap();
+/// should_fail().unwrap_err();
+/// ```
+#[macro_export]
+macro_rules! __result_of {
+    ($function: expr, $matcher: expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::result_of(
+            $function,
+            $matcher,
+            stringify!($function),
+        )
+    }};
+}
+
+/// Matches a value where the reference to the result of `callable` applied to
+/// the value matches the inner matcher.
+///
+/// The `callable` will be called twice, so make sure it is pure.
+/// ```
+/// use googletest::prelude::*;
+/// fn should_pass_1() -> googletest::Result<()> {
+///    verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
+///    Ok(())
+/// }
+///
+/// fn should_pass_2() -> googletest::Result<()> {
+///    verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))?; // Passes
+///    Ok(())
+/// }
+///
+/// fn should_fail() -> googletest::Result<()> {
+///    verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
+///    Ok(())
+/// }
+/// should_pass_1().unwrap();
+/// should_pass_2().unwrap();
+/// should_fail().unwrap_err();
+/// ```
+#[macro_export]
+macro_rules! __result_of_ref {
+    ($function: expr, $matcher: expr) => {{
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::result_of_ref(
+            $function,
+            $matcher,
+            stringify!($function),
+        )
+    }};
+}
+
+/// Items for use only by the declarative macros in this module.
+///
+/// **For internal use only. API stability is not guaranteed!**
+#[doc(hidden)]
+pub mod internal {
+    use crate::description::Description;
+    use crate::matcher::{Matcher, MatcherBase, MatcherResult};
+    use std::fmt::Debug;
+
+    pub fn result_of<Callable, InnerMatcher>(
+        callable: Callable,
+        inner_matcher: InnerMatcher,
+        callable_description: &'static str,
+    ) -> ResultOfMatcher<Callable, InnerMatcher> {
+        ResultOfMatcher { callable, inner_matcher, callable_description }
+    }
+
+    #[derive(MatcherBase)]
+    pub struct ResultOfMatcher<Callable, InnerMatcher> {
+        callable: Callable,
+        inner_matcher: InnerMatcher,
+        callable_description: &'static str,
+    }
+
+    impl<I: Copy + Debug, T: Debug + Copy, CallableT: Fn(I) -> T, InnerMatcherT: Matcher<T>>
+        Matcher<I> for ResultOfMatcher<CallableT, InnerMatcherT>
+    {
+        fn matches(&self, actual: I) -> MatcherResult {
+            self.inner_matcher.matches((self.callable)(actual))
+        }
+
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
+            Description::new()
+                .text(format!("by applying {},", self.callable_description))
+                .nested(self.inner_matcher.describe(matcher_result))
+        }
+
+        fn explain_match(&self, actual: I) -> Description {
+            let actual_result = (self.callable)(actual);
+            Description::new()
+                .text(format!("which, results into {actual_result:?}",))
+                .nested(self.describe(self.matches(actual)))
+        }
+    }
+
+    pub fn result_of_ref<Callable, InnerMatcher>(
+        callable: Callable,
+        inner_matcher: InnerMatcher,
+        callable_description: &'static str,
+    ) -> ResultOfRefMatcher<Callable, InnerMatcher> {
+        ResultOfRefMatcher { callable, inner_matcher, callable_description }
+    }
+    #[derive(MatcherBase)]
+    pub struct ResultOfRefMatcher<Callable, InnerMatcher> {
+        callable: Callable,
+        inner_matcher: InnerMatcher,
+        callable_description: &'static str,
+    }
+
+    impl<
+            I: Copy + Debug,
+            T: Debug,
+            Callable: Fn(I) -> T,
+            InnerMatcherT: for<'a> Matcher<&'a T>,
+        > Matcher<I> for ResultOfRefMatcher<Callable, InnerMatcherT>
+    {
+        fn matches(&self, actual: I) -> MatcherResult {
+            self.inner_matcher.matches(&(self.callable)(actual))
+        }
+
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
+            Description::new()
+                .text(format!("by applying {},", self.callable_description))
+                .nested(self.inner_matcher.describe(matcher_result))
+        }
+
+        fn explain_match(&self, actual: I) -> Description {
+            let actual_result = (self.callable)(actual);
+            Description::new()
+                .text(format!("which, results into {actual_result:?}",))
+                .nested(self.describe(self.matches(actual)))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::prelude::*;
+    use indoc::indoc;
+
+    #[test]
+    fn result_of_match_with_value() -> Result<()> {
+        verify_that!(1, result_of!(|value| value + 1, eq(2)))
+    }
+
+    #[test]
+    fn result_of_match_with_value_function() -> Result<()> {
+        fn inc_by_one(value: i32) -> i32 {
+            value + 1
+        }
+        verify_that!(1, result_of!(inc_by_one, eq(2)))
+    }
+
+    #[test]
+    fn result_of_match_with_different_value() -> Result<()> {
+        let result = verify_that!(0, result_of!(|value| value - 1, eq(2)));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                "
+                Value of: 0
+                Expected: by applying |value| value - 1,
+                  is equal to 2
+                Actual: 0,
+                  which, results into -1
+                    by applying |value| value - 1,
+                      isn't equal to 2
+                "
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_match_with_different_value_block_closure() -> Result<()> {
+        let result = verify_that!(0, result_of!(|value| { value - 1 }, eq(2)));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                "
+                Value of: 0
+                Expected: by applying |value| { value - 1 },
+                  is equal to 2
+                Actual: 0,
+                  which, results into -1
+                    by applying |value| { value - 1 },
+                      isn't equal to 2
+                "
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_match_with_different_value_multiline_closure() -> Result<()> {
+        let result = verify_that!(
+            0,
+            result_of!(
+                |value| {
+                    let dec = value - 1;
+                    let inc = dec + 1;
+                    inc - 2
+                },
+                eq(2)
+            )
+        );
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                "
+                Value of: 0
+                Expected: by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
+                  is equal to 2
+                Actual: 0,
+                  which, results into -2
+                    by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
+                      isn't equal to 2
+                "
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_match_with_different_value_function() -> Result<()> {
+        fn dec_by_one(value: i32) -> i32 {
+            value - 1
+        }
+        let result = verify_that!(0, result_of!(dec_by_one, eq(2)));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                "
+                Value of: 0
+                Expected: by applying dec_by_one,
+                  is equal to 2
+                Actual: 0,
+                  which, results into -1
+                    by applying dec_by_one,
+                      isn't equal to 2
+                "
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_ref_match_with_string_reference() -> Result<()> {
+        verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))
+    }
+
+    #[test]
+    fn result_of_ref_match_with_string_reference_function() -> Result<()> {
+        fn to_upper_case<S: AsRef<str>>(s: S) -> String {
+            s.as_ref().to_uppercase()
+        }
+        verify_that!("hello", result_of_ref!(to_upper_case, eq("HELLO")))
+    }
+
+    #[test]
+    fn result_of_ref_match_with_copy_types() -> Result<()> {
+        verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))
+    }
+
+    #[test]
+    fn result_of_ref_match_with_different_value() -> Result<()> {
+        let result = verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                r#"
+                Value of: "world"
+                Expected: by applying |s: &str| s.to_uppercase(),
+                  is equal to "HELLO"
+                Actual: "world",
+                  which, results into "WORLD"
+                    by applying |s: &str| s.to_uppercase(),
+                      isn't equal to "HELLO""#
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_ref_match_with_different_value_block_closure() -> Result<()> {
+        let result =
+            verify_that!("world", result_of_ref!(|s: &str| { s.to_uppercase() }, eq("HELLO")));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                r#"
+            Value of: "world"
+            Expected: by applying |s: &str| { s.to_uppercase() },
+              is equal to "HELLO"
+            Actual: "world",
+              which, results into "WORLD"
+                by applying |s: &str| { s.to_uppercase() },
+                  isn't equal to "HELLO"
+            "#
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_ref_match_with_different_value_function() -> Result<()> {
+        fn to_upper_case<S: AsRef<str>>(s: S) -> String {
+            s.as_ref().to_uppercase()
+        }
+        let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                r#"
+            Value of: "world"
+            Expected: by applying to_upper_case,
+              is equal to "HELLO"
+            Actual: "world",
+              which, results into "WORLD"
+                by applying to_upper_case,
+                  isn't equal to "HELLO"
+            "#
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_ref_match_different_with_closure_variable() -> Result<()> {
+        let to_upper_case = |s: &str| s.to_uppercase();
+        let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                r#"
+                Value of: "world"
+                Expected: by applying to_upper_case,
+                  is equal to "HELLO"
+                Actual: "world",
+                  which, results into "WORLD"
+                    by applying to_upper_case,
+                      isn't equal to "HELLO"
+            "#
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_ref_match_different_with_method_literal() -> Result<()> {
+        let result = verify_that!("world", result_of_ref!(str::to_uppercase, eq("HELLO")));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                r#"
+                Value of: "world"
+                Expected: by applying str::to_uppercase,
+                  is equal to "HELLO"
+                Actual: "world",
+                  which, results into "WORLD"
+                    by applying str::to_uppercase,
+                      isn't equal to "HELLO"
+            "#
+            ))))
+        )
+    }
+
+    #[test]
+    fn result_of_ref_match_different_with_function_return_closure() -> Result<()> {
+        fn upper_case() -> impl Fn(&str) -> String {
+            |s: &str| s.to_uppercase()
+        }
+        let result = verify_that!("world", result_of_ref!(upper_case(), eq("HELLO")));
+        verify_that!(
+            result,
+            err(displays_as(contains_substring(indoc!(
+                r#"
+            Value of: "world"
+            Expected: by applying upper_case(),
+              is equal to "HELLO"
+            Actual: "world",
+              which, results into "WORLD"
+                by applying upper_case(),
+                  isn't equal to "HELLO"
+            "#
+            ))))
+        )
+    }
+
+    #[test]
+    fn test_describe_simple() -> Result<()> {
+        let matcher = result_of!(|x| x + 1, eq(2));
+        let description = matcher.describe(matcher.matches(0));
+        verify_that!(
+            description,
+            displays_as(eq(indoc!(
+                r#"
+        by applying |x| x + 1,
+          isn't equal to 2"#
+            )))
+        )
+    }
+
+    #[test]
+    fn test_describe_complicated() -> Result<()> {
+        let matcher = result_of_ref!(
+            |s: &str| s.chars().collect::<Vec<_>>(),
+            each(predicate(char::is_ascii_alphabetic))
+        );
+        let description = matcher.describe(matcher.matches("A quick brown fox"));
+        verify_that!(
+            description,
+            displays_as(eq(indoc!(
+                r#"
+        by applying |s: &str| s.chars().collect::<Vec<_>>(),
+          contains no element that matches"#
+            )))
+        )
+    }
+}
diff --git a/crates/googletest/src/matchers/some_matcher.rs b/crates/googletest/src/matchers/some_matcher.rs
index 905aa17..8c926f9 100644
--- a/crates/googletest/src/matchers/some_matcher.rs
+++ b/crates/googletest/src/matchers/some_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches an `Option` containing a value matched by `inner`.
 ///
@@ -38,23 +38,51 @@
 /// # should_fail_1().unwrap_err();
 /// # should_fail_2().unwrap_err();
 /// ```
-pub fn some<T: Debug>(inner: impl Matcher<ActualT = T>) -> impl Matcher<ActualT = Option<T>> {
-    SomeMatcher { inner, phantom: Default::default() }
+pub fn some<Inner>(inner: Inner) -> SomeMatcher<Inner> {
+    SomeMatcher { inner }
 }
 
-struct SomeMatcher<T, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct SomeMatcher<InnerMatcherT> {
     inner: InnerMatcherT,
-    phantom: PhantomData<T>,
 }
 
-impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher for SomeMatcher<T, InnerMatcherT> {
-    type ActualT = Option<T>;
+impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<Option<T>> for SomeMatcher<InnerMatcherT> {
+    fn matches(&self, actual: Option<T>) -> MatcherResult {
+        actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
+    }
 
-    fn matches(&self, actual: &Option<T>) -> MatcherResult {
+    fn explain_match(&self, actual: Option<T>) -> Description {
+        match (self.matches(actual), actual) {
+            (_, Some(t)) => {
+                Description::new().text("which has a value").nested(self.inner.explain_match(t))
+            }
+            (_, None) => "which is None".into(),
+        }
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        match matcher_result {
+            MatcherResult::Match => {
+                format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
+            }
+            MatcherResult::NoMatch => format!(
+                "is None or has a value which {}",
+                self.inner.describe(MatcherResult::NoMatch)
+            )
+            .into(),
+        }
+    }
+}
+
+impl<'a, T: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a Option<T>>
+    for SomeMatcher<InnerMatcherT>
+{
+    fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
         actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
     }
 
-    fn explain_match(&self, actual: &Option<T>) -> Description {
+    fn explain_match(&self, actual: &'a Option<T>) -> Description {
         match (self.matches(actual), actual) {
             (_, Some(t)) => {
                 Description::new().text("which has a value").nested(self.inner.explain_match(t))
@@ -79,8 +107,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::some;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -88,7 +115,7 @@
     fn some_matches_option_with_value() -> Result<()> {
         let matcher = some(eq(1));
 
-        let result = matcher.matches(&Some(1));
+        let result = matcher.matches(Some(1));
 
         verify_that!(result, eq(MatcherResult::Match))
     }
@@ -97,21 +124,36 @@
     fn some_does_not_match_option_with_wrong_value() -> Result<()> {
         let matcher = some(eq(1));
 
-        let result = matcher.matches(&Some(0));
+        let result = matcher.matches(Some(0));
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
     fn some_does_not_match_option_with_none() -> Result<()> {
-        let matcher = some(eq::<i32, _>(1));
+        let matcher = some(eq(1));
 
-        let result = matcher.matches(&None);
+        let result = matcher.matches(None::<i32>);
 
         verify_that!(result, eq(MatcherResult::NoMatch))
     }
 
     #[test]
+    fn some_matches_option_with_by_ref_value() -> Result<()> {
+        verify_that!(Some("123".to_string()), some(eq("123")))
+    }
+
+    #[test]
+    fn some_does_not_match_option_with_wrong_by_ref_value() -> Result<()> {
+        verify_that!(Some("321".to_string()), not(some(eq("123"))))
+    }
+
+    #[test]
+    fn some_does_not_match_option_with_by_ref_none() -> Result<()> {
+        verify_that!(None::<String>, not(some(eq("123"))))
+    }
+
+    #[test]
     fn some_full_error_message() -> Result<()> {
         let result = verify_that!(Some(2), some(eq(1)));
         verify_that!(
@@ -131,7 +173,7 @@
     #[test]
     fn some_describe_matches() -> Result<()> {
         verify_that!(
-            some(eq::<i32, _>(1)).describe(MatcherResult::Match),
+            Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::Match),
             displays_as(eq("has a value which is equal to 1"))
         )
     }
@@ -139,20 +181,36 @@
     #[test]
     fn some_describe_does_not_match() -> Result<()> {
         verify_that!(
-            some(eq::<i32, _>(1)).describe(MatcherResult::NoMatch),
+            Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::NoMatch),
             displays_as(eq("is None or has a value which isn't equal to 1"))
         )
     }
 
     #[test]
+    fn some_describe_matches_of_by_ref() -> Result<()> {
+        verify_that!(
+            Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::Match),
+            displays_as(eq("has a value which is equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn some_describe_does_not_match_of_by_ref() -> Result<()> {
+        verify_that!(
+            Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::NoMatch),
+            displays_as(eq("is None or has a value which isn't equal to \"123\""))
+        )
+    }
+
+    #[test]
     fn some_explain_match_with_none() -> Result<()> {
-        verify_that!(some(eq::<i32, _>(1)).explain_match(&None), displays_as(eq("which is None")))
+        verify_that!(some(eq(1)).explain_match(None::<i32>), displays_as(eq("which is None")))
     }
 
     #[test]
     fn some_explain_match_with_some_success() -> Result<()> {
         verify_that!(
-            some(eq(1)).explain_match(&Some(1)),
+            some(eq(1)).explain_match(Some(1)),
             displays_as(eq("which has a value\n  which is equal to 1"))
         )
     }
@@ -160,8 +218,32 @@
     #[test]
     fn some_explain_match_with_some_fail() -> Result<()> {
         verify_that!(
-            some(eq(1)).explain_match(&Some(2)),
+            some(eq(1)).explain_match(Some(2)),
             displays_as(eq("which has a value\n  which isn't equal to 1"))
         )
     }
+
+    #[test]
+    fn some_explain_match_with_none_by_ref() -> Result<()> {
+        verify_that!(
+            some(eq("123")).explain_match(&None::<String>),
+            displays_as(eq("which is None"))
+        )
+    }
+
+    #[test]
+    fn some_explain_match_with_some_success_by_ref() -> Result<()> {
+        verify_that!(
+            some(eq("123")).explain_match(&Some("123".to_string())),
+            displays_as(eq("which has a value\n  which is equal to \"123\""))
+        )
+    }
+
+    #[test]
+    fn some_explain_match_with_some_fail_by_ref() -> Result<()> {
+        verify_that!(
+            some(eq("123")).explain_match(&Some("321".to_string())),
+            displays_as(eq("which has a value\n  which isn't equal to \"123\""))
+        )
+    }
 }
diff --git a/crates/googletest/src/matchers/str_matcher.rs b/crates/googletest/src/matchers/str_matcher.rs
index b624d44..aa58d4c 100644
--- a/crates/googletest/src/matchers/str_matcher.rs
+++ b/crates/googletest/src/matchers/str_matcher.rs
@@ -14,16 +14,15 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
     matcher_support::{
         edit_distance,
         summarize_diff::{create_diff, create_diff_reversed},
     },
-    matchers::{eq_deref_of_matcher::EqDerefOfMatcher, eq_matcher::EqMatcher},
+    matchers::eq_matcher::EqMatcher,
 };
 use std::borrow::Cow;
 use std::fmt::Debug;
-use std::marker::PhantomData;
 use std::ops::Deref;
 
 /// Matches a string containing a given substring.
@@ -59,11 +58,10 @@
 /// > and expected values when matching strings while
 /// > [`ignoring_ascii_case`][StrMatcherConfigurator::ignoring_ascii_case] is
 /// > set.
-pub fn contains_substring<A: ?Sized, T>(expected: T) -> StrMatcher<A, T> {
+pub fn contains_substring<T>(expected: T) -> StrMatcher<T> {
     StrMatcher {
         configuration: Configuration { mode: MatchMode::Contains, ..Default::default() },
         expected,
-        phantom: Default::default(),
     }
 }
 
@@ -99,11 +97,10 @@
 ///
 /// See the [`StrMatcherConfigurator`] extension trait for more options on how
 /// the string is matched.
-pub fn starts_with<A: ?Sized, T>(expected: T) -> StrMatcher<A, T> {
+pub fn starts_with<T>(expected: T) -> StrMatcher<T> {
     StrMatcher {
         configuration: Configuration { mode: MatchMode::StartsWith, ..Default::default() },
         expected,
-        phantom: Default::default(),
     }
 }
 
@@ -139,11 +136,10 @@
 ///
 /// See the [`StrMatcherConfigurator`] extension trait for more options on how
 /// the string is matched.
-pub fn ends_with<A: ?Sized, T>(expected: T) -> StrMatcher<A, T> {
+pub fn ends_with<T>(expected: T) -> StrMatcher<T> {
     StrMatcher {
         configuration: Configuration { mode: MatchMode::EndsWith, ..Default::default() },
         expected,
-        phantom: Default::default(),
     }
 }
 
@@ -152,7 +148,7 @@
 /// Matchers which match against string values and, through configuration,
 /// specialise to [`StrMatcher`] implement this trait. That includes
 /// [`EqMatcher`] and [`StrMatcher`].
-pub trait StrMatcherConfigurator<ActualT: ?Sized, ExpectedT> {
+pub trait StrMatcherConfigurator<ExpectedT> {
     /// Configures the matcher to ignore any leading whitespace in either the
     /// actual or the expected value.
     ///
@@ -171,7 +167,7 @@
     /// When all other configuration options are left as the defaults, this is
     /// equivalent to invoking [`str::trim_start`] on both the expected and
     /// actual value.
-    fn ignoring_leading_whitespace(self) -> StrMatcher<ActualT, ExpectedT>;
+    fn ignoring_leading_whitespace(self) -> StrMatcher<ExpectedT>;
 
     /// Configures the matcher to ignore any trailing whitespace in either the
     /// actual or the expected value.
@@ -191,7 +187,7 @@
     /// When all other configuration options are left as the defaults, this is
     /// equivalent to invoking [`str::trim_end`] on both the expected and
     /// actual value.
-    fn ignoring_trailing_whitespace(self) -> StrMatcher<ActualT, ExpectedT>;
+    fn ignoring_trailing_whitespace(self) -> StrMatcher<ExpectedT>;
 
     /// Configures the matcher to ignore both leading and trailing whitespace in
     /// either the actual or the expected value.
@@ -215,7 +211,7 @@
     /// When all other configuration options are left as the defaults, this is
     /// equivalent to invoking [`str::trim`] on both the expected and actual
     /// value.
-    fn ignoring_outer_whitespace(self) -> StrMatcher<ActualT, ExpectedT>;
+    fn ignoring_outer_whitespace(self) -> StrMatcher<ExpectedT>;
 
     /// Configures the matcher to ignore ASCII case when comparing values.
     ///
@@ -237,7 +233,7 @@
     ///
     /// This is **not guaranteed** to match strings with differing upper/lower
     /// case characters outside of the codepoints 0-127 covered by ASCII.
-    fn ignoring_ascii_case(self) -> StrMatcher<ActualT, ExpectedT>;
+    fn ignoring_ascii_case(self) -> StrMatcher<ExpectedT>;
 
     /// Configures the matcher to match only strings which otherwise satisfy the
     /// conditions a number times matched by the matcher `times`.
@@ -272,10 +268,7 @@
     /// This is only meaningful when the matcher was constructed with
     /// [`contains_substring`]. This method will panic when it is used with any
     /// other matcher construction.
-    fn times(
-        self,
-        times: impl Matcher<ActualT = usize> + 'static,
-    ) -> StrMatcher<ActualT, ExpectedT>;
+    fn times(self, times: impl Matcher<usize> + 'static) -> StrMatcher<ExpectedT>;
 }
 
 /// A matcher which matches equality or containment of a string-like value in a
@@ -287,20 +280,18 @@
 ///  * [`contains_substring`],
 ///  * [`starts_with`],
 ///  * [`ends_with`].
-pub struct StrMatcher<ActualT: ?Sized, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct StrMatcher<ExpectedT> {
     expected: ExpectedT,
     configuration: Configuration,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ExpectedT, ActualT> Matcher for StrMatcher<ActualT, ExpectedT>
+impl<ExpectedT, ActualT> Matcher<ActualT> for StrMatcher<ExpectedT>
 where
     ExpectedT: Deref<Target = str> + Debug,
-    ActualT: AsRef<str> + Debug + ?Sized,
+    ActualT: AsRef<str> + Debug + Copy,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         self.configuration.do_strings_match(self.expected.deref(), actual.as_ref()).into()
     }
 
@@ -308,15 +299,15 @@
         self.configuration.describe(matcher_result, self.expected.deref())
     }
 
-    fn explain_match(&self, actual: &ActualT) -> Description {
+    fn explain_match(&self, actual: ActualT) -> Description {
         self.configuration.explain_match(self.expected.deref(), actual.as_ref())
     }
 }
 
-impl<ActualT: ?Sized, ExpectedT, MatcherT: Into<StrMatcher<ActualT, ExpectedT>>>
-    StrMatcherConfigurator<ActualT, ExpectedT> for MatcherT
+impl<ExpectedT, MatcherT: Into<StrMatcher<ExpectedT>>> StrMatcherConfigurator<ExpectedT>
+    for MatcherT
 {
-    fn ignoring_leading_whitespace(self) -> StrMatcher<ActualT, ExpectedT> {
+    fn ignoring_leading_whitespace(self) -> StrMatcher<ExpectedT> {
         let existing = self.into();
         StrMatcher {
             configuration: existing.configuration.ignoring_leading_whitespace(),
@@ -324,7 +315,7 @@
         }
     }
 
-    fn ignoring_trailing_whitespace(self) -> StrMatcher<ActualT, ExpectedT> {
+    fn ignoring_trailing_whitespace(self) -> StrMatcher<ExpectedT> {
         let existing = self.into();
         StrMatcher {
             configuration: existing.configuration.ignoring_trailing_whitespace(),
@@ -332,20 +323,17 @@
         }
     }
 
-    fn ignoring_outer_whitespace(self) -> StrMatcher<ActualT, ExpectedT> {
+    fn ignoring_outer_whitespace(self) -> StrMatcher<ExpectedT> {
         let existing = self.into();
         StrMatcher { configuration: existing.configuration.ignoring_outer_whitespace(), ..existing }
     }
 
-    fn ignoring_ascii_case(self) -> StrMatcher<ActualT, ExpectedT> {
+    fn ignoring_ascii_case(self) -> StrMatcher<ExpectedT> {
         let existing = self.into();
         StrMatcher { configuration: existing.configuration.ignoring_ascii_case(), ..existing }
     }
 
-    fn times(
-        self,
-        times: impl Matcher<ActualT = usize> + 'static,
-    ) -> StrMatcher<ActualT, ExpectedT> {
+    fn times(self, times: impl Matcher<usize> + 'static) -> StrMatcher<ExpectedT> {
         let existing = self.into();
         if !matches!(existing.configuration.mode, MatchMode::Contains) {
             panic!("The times() configurator is only meaningful with contains_substring().");
@@ -354,25 +342,19 @@
     }
 }
 
-impl<A: ?Sized, T: Deref<Target = str>> From<EqMatcher<A, T>> for StrMatcher<A, T> {
-    fn from(value: EqMatcher<A, T>) -> Self {
+impl<T: Deref<Target = str>> From<EqMatcher<T>> for StrMatcher<T> {
+    fn from(value: EqMatcher<T>) -> Self {
         Self::with_default_config(value.expected)
     }
 }
 
-impl<A: ?Sized, T: Deref<Target = str>> From<EqDerefOfMatcher<A, T>> for StrMatcher<A, T> {
-    fn from(value: EqDerefOfMatcher<A, T>) -> Self {
-        Self::with_default_config(value.expected)
-    }
-}
-
-impl<A: ?Sized, T> StrMatcher<A, T> {
+impl<T> StrMatcher<T> {
     /// Returns a [`StrMatcher`] with a default configuration to match against
     /// the given expected value.
     ///
     /// This default configuration is sensitive to whitespace and case.
     fn with_default_config(expected: T) -> Self {
-        Self { expected, configuration: Default::default(), phantom: Default::default() }
+        Self { expected, configuration: Default::default() }
     }
 }
 
@@ -387,7 +369,7 @@
     ignore_leading_whitespace: bool,
     ignore_trailing_whitespace: bool,
     case_policy: CasePolicy,
-    times: Option<Box<dyn Matcher<ActualT = usize>>>,
+    times: Option<Box<dyn Matcher<usize>>>,
 }
 
 #[derive(Clone)]
@@ -461,13 +443,13 @@
             // Split returns an iterator over the "boundaries" left and right of the
             // substring to be matched, of which there is one more than the number of
             // substrings.
-            matches!(times.matches(&(actual.split(expected).count() - 1)), MatcherResult::Match)
+            matches!(times.matches(actual.split(expected).count() - 1), MatcherResult::Match)
         } else {
             actual.contains(expected)
         }
     }
 
-    // StrMatcher::describe redirects immediately to this function.
+    // StrMatcher::<str>::describe redirects immediately to this function.
     fn describe(&self, matcher_result: MatcherResult, expected: &str) -> Description {
         let mut addenda: Vec<Cow<'static, str>> = Vec::with_capacity(3);
         match (self.ignore_leading_whitespace, self.ignore_trailing_whitespace) {
@@ -551,7 +533,11 @@
             MatchMode::EndsWith => create_diff_reversed(actual, expected, self.mode.to_diff_mode()),
         };
 
-        format!("{default_explanation}\n{diff}").into()
+        if diff.is_empty() {
+            format!("{default_explanation}").into()
+        } else {
+            format!("{default_explanation}\n\n{diff}").into()
+        }
     }
 
     fn ignoring_leading_whitespace(self) -> Self {
@@ -570,7 +556,7 @@
         Self { case_policy: CasePolicy::IgnoreAscii, ..self }
     }
 
-    fn times(self, times: impl Matcher<ActualT = usize> + 'static) -> Self {
+    fn times(self, times: impl Matcher<usize> + 'static) -> Self {
         Self { times: Some(Box::new(times)), ..self }
     }
 }
@@ -589,8 +575,7 @@
 
 #[cfg(test)]
 mod tests {
-    use super::{contains_substring, ends_with, starts_with, StrMatcher, StrMatcherConfigurator};
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
 
@@ -713,23 +698,13 @@
     }
 
     #[test]
-    fn allows_ignoring_ascii_case_from_eq_deref_of_str_slice() -> Result<()> {
-        verify_that!("A string", eq_deref_of("A STRING").ignoring_ascii_case())
-    }
-
-    #[test]
-    fn allows_ignoring_ascii_case_from_eq_deref_of_owned_string() -> Result<()> {
-        verify_that!("A string", eq_deref_of("A STRING".to_string()).ignoring_ascii_case())
-    }
-
-    #[test]
     fn matches_string_containing_expected_value_in_contains_mode() -> Result<()> {
         verify_that!("Some string", contains_substring("str"))
     }
 
     #[test]
-    fn matches_string_containing_expected_value_in_contains_mode_while_ignoring_ascii_case()
-    -> Result<()> {
+    fn matches_string_containing_expected_value_in_contains_mode_while_ignoring_ascii_case(
+    ) -> Result<()> {
         verify_that!("Some string", contains_substring("STR").ignoring_ascii_case())
     }
 
@@ -810,48 +785,45 @@
 
     #[test]
     fn describes_itself_for_matching_result() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string");
+        let matcher = StrMatcher::with_default_config("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is equal to \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_for_non_matching_result() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string");
+        let matcher = StrMatcher::with_default_config("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::NoMatch),
+            Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
             displays_as(eq("isn't equal to \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_for_matching_result_ignoring_leading_whitespace() -> Result<()> {
-        let matcher: StrMatcher<&str, _> =
-            StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
+        let matcher = StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is equal to \"A string\" (ignoring leading whitespace)"))
         )
     }
 
     #[test]
     fn describes_itself_for_non_matching_result_ignoring_leading_whitespace() -> Result<()> {
-        let matcher: StrMatcher<&str, _> =
-            StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
+        let matcher = StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::NoMatch),
+            Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
             displays_as(eq("isn't equal to \"A string\" (ignoring leading whitespace)"))
         )
     }
 
     #[test]
     fn describes_itself_for_matching_result_ignoring_trailing_whitespace() -> Result<()> {
-        let matcher: StrMatcher<&str, _> =
-            StrMatcher::with_default_config("A string").ignoring_trailing_whitespace();
+        let matcher = StrMatcher::with_default_config("A string").ignoring_trailing_whitespace();
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is equal to \"A string\" (ignoring trailing whitespace)"))
         )
     }
@@ -859,32 +831,30 @@
     #[test]
     fn describes_itself_for_matching_result_ignoring_leading_and_trailing_whitespace() -> Result<()>
     {
-        let matcher: StrMatcher<&str, _> =
-            StrMatcher::with_default_config("A string").ignoring_outer_whitespace();
+        let matcher = StrMatcher::with_default_config("A string").ignoring_outer_whitespace();
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is equal to \"A string\" (ignoring leading and trailing whitespace)"))
         )
     }
 
     #[test]
     fn describes_itself_for_matching_result_ignoring_ascii_case() -> Result<()> {
-        let matcher: StrMatcher<&str, _> =
-            StrMatcher::with_default_config("A string").ignoring_ascii_case();
+        let matcher = StrMatcher::with_default_config("A string").ignoring_ascii_case();
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("is equal to \"A string\" (ignoring ASCII case)"))
         )
     }
 
     #[test]
-    fn describes_itself_for_matching_result_ignoring_ascii_case_and_leading_whitespace()
-    -> Result<()> {
-        let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string")
+    fn describes_itself_for_matching_result_ignoring_ascii_case_and_leading_whitespace(
+    ) -> Result<()> {
+        let matcher = StrMatcher::with_default_config("A string")
             .ignoring_leading_whitespace()
             .ignoring_ascii_case();
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq(
                 "is equal to \"A string\" (ignoring leading whitespace, ignoring ASCII case)"
             ))
@@ -893,63 +863,63 @@
 
     #[test]
     fn describes_itself_for_matching_result_in_contains_mode() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = contains_substring("A string");
+        let matcher = contains_substring("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("contains a substring \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_for_non_matching_result_in_contains_mode() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = contains_substring("A string");
+        let matcher = contains_substring("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::NoMatch),
+            Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
             displays_as(eq("does not contain a substring \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_with_count_number() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = contains_substring("A string").times(gt(2));
+        let matcher = contains_substring("A string").times(gt(2));
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("contains a substring \"A string\" (count is greater than 2)"))
         )
     }
 
     #[test]
     fn describes_itself_for_matching_result_in_starts_with_mode() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = starts_with("A string");
+        let matcher = starts_with("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("starts with prefix \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_for_non_matching_result_in_starts_with_mode() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = starts_with("A string");
+        let matcher = starts_with("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::NoMatch),
+            Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
             displays_as(eq("does not start with \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_for_matching_result_in_ends_with_mode() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = ends_with("A string");
+        let matcher = ends_with("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&str>::describe(&matcher, MatcherResult::Match),
             displays_as(eq("ends with suffix \"A string\""))
         )
     }
 
     #[test]
     fn describes_itself_for_non_matching_result_in_ends_with_mode() -> Result<()> {
-        let matcher: StrMatcher<&str, _> = ends_with("A string");
+        let matcher = ends_with("A string");
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::NoMatch),
+            Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
             displays_as(eq("does not end with \"A string\""))
         )
     }
@@ -1019,8 +989,8 @@
     }
 
     #[test]
-    fn match_explanation_for_starts_with_includes_both_versions_of_differing_last_line()
-    -> Result<()> {
+    fn match_explanation_for_starts_with_includes_both_versions_of_differing_last_line(
+    ) -> Result<()> {
         let result = verify_that!(
             indoc!(
                 "
@@ -1121,8 +1091,8 @@
     }
 
     #[test]
-    fn match_explanation_for_contains_substring_shows_diff_when_first_and_last_line_are_incomplete()
-    -> Result<()> {
+    fn match_explanation_for_contains_substring_shows_diff_when_first_and_last_line_are_incomplete(
+    ) -> Result<()> {
         let result = verify_that!(
             indoc!(
                 "
diff --git a/crates/googletest/src/matchers/subset_of_matcher.rs b/crates/googletest/src/matchers/subset_of_matcher.rs
index 24c00d8..2beaf2d 100644
--- a/crates/googletest/src/matchers/subset_of_matcher.rs
+++ b/crates/googletest/src/matchers/subset_of_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a container all of whose items are in the given container
 /// `superset`.
@@ -24,23 +24,23 @@
 /// The element type `ElementT` must implement `PartialEq` to allow element
 /// comparison.
 ///
-/// `ActualT` and `ExpectedT` can each be any container a reference to which
+/// `ActualT` and `ExpectedT` can each be any container which
 /// implements `IntoIterator`. For instance, `T` can be a common container like
-/// `Vec` or arrays. They need not be the same container type.
+/// `&Vec` or arrays. They need not be the same container type.
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # use std::collections::HashSet;
 /// # fn should_pass_1() -> Result<()> {
 /// let value = vec![1, 2, 3];
-/// verify_that!(value, subset_of([1, 2, 3, 4]))?;  // Passes
+/// verify_that!(value, subset_of([&1, &2, &3, &4]))?;  // Passes
 /// let array_value = [1, 2, 3];
 /// verify_that!(array_value, subset_of([1, 2, 3, 4]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
 /// # let value = vec![1, 2, 3];
-/// verify_that!(value, subset_of([1, 2]))?;  // Fails: 3 is not in the superset
+/// verify_that!(value, subset_of([&1, &2]))?;  // Fails: 3 is not in the superset
 /// #     Ok(())
 /// # }
 /// # should_pass_1().unwrap();
@@ -48,7 +48,7 @@
 ///
 /// # fn should_pass_2() -> Result<()> {
 /// let value: HashSet<i32> = [1, 2, 3].into();
-/// verify_that!(value, subset_of([1, 2, 3]))?;  // Passes
+/// verify_that!(value, subset_of([&1, &2, &3]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass_2().unwrap();
@@ -60,20 +60,8 @@
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// let value: Vec<i32> = vec![0, 0, 1];
-/// verify_that!(value, subset_of([0, 1]))?;  // Passes
-/// verify_that!(value, subset_of([0, 1, 1]))?;  // Passes
-/// #     Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// One can also verify the contents of a slice by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, subset_of([1, 2, 3]))?;
+/// verify_that!(value, subset_of([&0, &1]))?;  // Passes
+/// verify_that!(value, subset_of([&0, &1, &1]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -83,43 +71,35 @@
 /// runtime proportional to the *product* of the sizes of the actual and
 /// expected containers as well as the time to check equality of each pair of
 /// items. It should not be used on especially large containers.
-pub fn subset_of<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug>(
-    superset: ExpectedT,
-) -> impl Matcher<ActualT = ActualT>
-where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
-    for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
-{
-    SubsetOfMatcher::<ActualT, _> { superset, phantom: Default::default() }
+pub fn subset_of<ExpectedT>(superset: ExpectedT) -> SubsetOfMatcher<ExpectedT> {
+    SubsetOfMatcher { superset }
 }
 
-struct SubsetOfMatcher<ActualT: ?Sized, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct SubsetOfMatcher<ExpectedT> {
     superset: ExpectedT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher
-    for SubsetOfMatcher<ActualT, ExpectedT>
+impl<ElementT: Debug + PartialEq + Copy, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
+    for SubsetOfMatcher<ExpectedT>
 where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
+    ActualT: IntoIterator<Item = ElementT>,
     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         for actual_item in actual {
-            if self.expected_is_missing(actual_item) {
+            if self.expected_is_missing(&actual_item) {
                 return MatcherResult::NoMatch;
             }
         }
         MatcherResult::Match
     }
 
-    fn explain_match(&self, actual: &ActualT) -> Description {
+    fn explain_match(&self, actual: ActualT) -> Description {
         let unexpected_elements = actual
             .into_iter()
             .enumerate()
-            .filter(|&(_, actual_item)| self.expected_is_missing(actual_item))
+            .filter(|item| self.expected_is_missing(&item.1))
             .map(|(idx, actual_item)| format!("{actual_item:#?} at #{idx}"))
             .collect::<Vec<_>>();
 
@@ -138,18 +118,17 @@
     }
 }
 
-impl<ActualT: ?Sized, ElementT: PartialEq, ExpectedT> SubsetOfMatcher<ActualT, ExpectedT>
+impl<ElementT: PartialEq, ExpectedT> SubsetOfMatcher<ExpectedT>
 where
     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
 {
     fn expected_is_missing(&self, needle: &ElementT) -> bool {
-        !self.superset.into_iter().any(|item| *item == *needle)
+        !self.superset.into_iter().any(|item| item == needle)
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::subset_of;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::HashSet;
@@ -163,54 +142,54 @@
     #[test]
     fn subset_of_matches_vec_with_one_element() -> Result<()> {
         let value = vec![1];
-        verify_that!(value, subset_of([1]))
+        verify_that!(value, subset_of([&1]))
     }
 
     #[test]
     fn subset_of_matches_vec_with_two_elements() -> Result<()> {
         let value = vec![1, 2];
-        verify_that!(value, subset_of([1, 2]))
+        verify_that!(value, subset_of([&1, &2]))
     }
 
     #[test]
     fn subset_of_matches_vec_when_expected_has_excess_element() -> Result<()> {
         let value = vec![1, 2];
-        verify_that!(value, subset_of([1, 2, 3]))
+        verify_that!(value, subset_of([&1, &2, &3]))
     }
 
     #[test]
     fn subset_of_matches_vec_when_expected_has_excess_element_first() -> Result<()> {
         let value = vec![1, 2];
-        verify_that!(value, subset_of([3, 1, 2]))
+        verify_that!(value, subset_of([&3, &1, &2]))
     }
 
     #[test]
     fn subset_of_matches_slice_with_one_element() -> Result<()> {
         let value = &[1];
-        verify_that!(*value, subset_of([1]))
+        verify_that!(value, subset_of([&1]))
     }
 
     #[test]
     fn subset_of_matches_hash_set_with_one_element() -> Result<()> {
         let value: HashSet<i32> = [1].into();
-        verify_that!(value, subset_of([1]))
+        verify_that!(value, subset_of([&1]))
     }
 
     #[test]
     fn subset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
         let value = vec![0];
-        verify_that!(value, not(subset_of([1])))
+        verify_that!(value, not(subset_of([&1])))
     }
 
     #[test]
     fn subset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
         let value = vec![2, 0];
-        verify_that!(value, not(subset_of([2])))
+        verify_that!(value, not(subset_of([&2])))
     }
 
     #[test]
     fn subset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
-        let result = verify_that!(vec![0, 2, 3], subset_of([1, 2, 3]));
+        let result = verify_that!(vec![0, 2, 3], subset_of([&1, &2, &3]));
 
         verify_that!(
             result,
@@ -231,7 +210,7 @@
 
     #[test]
     fn subset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
-        let result = verify_that!(vec![1, 0, 3], subset_of([1, 2, 3]));
+        let result = verify_that!(vec![1, 0, 3], subset_of([&1, &2, &3]));
 
         verify_that!(
             result,
@@ -252,7 +231,7 @@
 
     #[test]
     fn subset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
-        let result = verify_that!(vec![0, 0, 3], subset_of([1, 2, 3]));
+        let result = verify_that!(vec![0, 0, 3], subset_of([&1, &2, &3]));
 
         verify_that!(
             result,
diff --git a/crates/googletest/src/matchers/superset_of_matcher.rs b/crates/googletest/src/matchers/superset_of_matcher.rs
index d1e9d72..8d28e45 100644
--- a/crates/googletest/src/matchers/superset_of_matcher.rs
+++ b/crates/googletest/src/matchers/superset_of_matcher.rs
@@ -14,9 +14,9 @@
 
 use crate::{
     description::Description,
-    matcher::{Matcher, MatcherResult},
+    matcher::{Matcher, MatcherBase, MatcherResult},
 };
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
 
 /// Matches a container containing all of the items in the given container
 /// `subset`.
@@ -24,24 +24,24 @@
 /// The element type `ElementT` must implement `PartialEq` to allow element
 /// comparison.
 ///
-/// `ActualT` and `ExpectedT` can each be any container a reference to which
-/// implements `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
-/// common container like `Vec` or arrays. They need not be the same container
-/// type.
+/// `ActualT` and `ExpectedT` can each be any container which implements
+/// `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
+/// common container like `&Vec`, arrays or slices. They need not be the same
+/// container type.
 ///
 /// ```
 /// # use googletest::prelude::*;
 /// # use std::collections::HashSet;
 /// # fn should_pass_1() -> Result<()> {
 /// let value = vec![1, 2, 3];
-/// verify_that!(value, superset_of([1, 2]))?;  // Passes
+/// verify_that!(value, superset_of([&1, &2]))?;  // Passes
 /// let array_value = [1, 2, 3];
 /// verify_that!(array_value, superset_of([1, 2]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
 /// # let value = vec![1, 2, 3];
-/// verify_that!(value, superset_of([1, 2, 4]))?;  // Fails: 4 is not in the subset
+/// verify_that!(value, superset_of([&1, &2, &4]))?;  // Fails: 4 is not in the subset
 /// #     Ok(())
 /// # }
 /// # should_pass_1().unwrap();
@@ -49,7 +49,7 @@
 ///
 /// # fn should_pass_2() -> Result<()> {
 /// let value: HashSet<i32> = [1, 2, 3].into();
-/// verify_that!(value, superset_of([1, 2, 3]))?;  // Passes
+/// verify_that!(value, superset_of([&1, &2, &3]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass_2().unwrap();
@@ -61,20 +61,8 @@
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
 /// let value: Vec<i32> = vec![0, 0, 1];
-/// verify_that!(value, superset_of([0, 1]))?;  // Passes
-/// verify_that!(value, superset_of([0, 1, 1]))?;  // Passes
-/// #     Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// One can also verify the contents of a slice by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, superset_of([1, 2, 3]))?;
+/// verify_that!(value, superset_of([&0, &1]))?;  // Passes
+/// verify_that!(value, superset_of([&0, &1, &1]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -84,30 +72,22 @@
 /// runtime proportional to the *product* of the sizes of the actual and
 /// expected containers as well as the time to check equality of each pair of
 /// items. It should not be used on especially large containers.
-pub fn superset_of<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug>(
-    subset: ExpectedT,
-) -> impl Matcher<ActualT = ActualT>
-where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
-    for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
-{
-    SupersetOfMatcher::<ActualT, _> { subset, phantom: Default::default() }
+pub fn superset_of<ExpectedT>(subset: ExpectedT) -> SupersetOfMatcher<ExpectedT> {
+    SupersetOfMatcher { subset }
 }
 
-struct SupersetOfMatcher<ActualT: ?Sized, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct SupersetOfMatcher<ExpectedT> {
     subset: ExpectedT,
-    phantom: PhantomData<ActualT>,
 }
 
-impl<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher
-    for SupersetOfMatcher<ActualT, ExpectedT>
+impl<ElementT: Debug + Copy + PartialEq, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
+    for SupersetOfMatcher<ExpectedT>
 where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
+    ActualT: IntoIterator<Item = ElementT>,
     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
 {
-    type ActualT = ActualT;
-
-    fn matches(&self, actual: &ActualT) -> MatcherResult {
+    fn matches(&self, actual: ActualT) -> MatcherResult {
         for expected_item in &self.subset {
             if actual_is_missing(actual, expected_item) {
                 return MatcherResult::NoMatch;
@@ -116,11 +96,11 @@
         MatcherResult::Match
     }
 
-    fn explain_match(&self, actual: &ActualT) -> Description {
+    fn explain_match(&self, actual: ActualT) -> Description {
         let missing_items: Vec<_> = self
             .subset
             .into_iter()
-            .filter(|expected_item| actual_is_missing(actual, expected_item))
+            .filter(|expected_item| actual_is_missing(actual, *expected_item))
             .map(|expected_item| format!("{expected_item:#?}"))
             .collect();
         match missing_items.len() {
@@ -138,19 +118,14 @@
     }
 }
 
-fn actual_is_missing<ElementT: PartialEq, ActualT: ?Sized>(
-    actual: &ActualT,
-    needle: &ElementT,
-) -> bool
+fn actual_is_missing<ElementT: PartialEq, ActualT>(actual: ActualT, needle: &ElementT) -> bool
 where
-    for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
+    ActualT: IntoIterator<Item = ElementT>,
 {
-    !actual.into_iter().any(|item| *item == *needle)
+    !actual.into_iter().any(|item| &item == needle)
 }
-
 #[cfg(test)]
 mod tests {
-    use super::superset_of;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::HashSet;
@@ -164,54 +139,54 @@
     #[test]
     fn superset_of_matches_vec_with_one_element() -> Result<()> {
         let value = vec![1];
-        verify_that!(value, superset_of([1]))
+        verify_that!(value, superset_of([&1]))
     }
 
     #[test]
     fn superset_of_matches_vec_with_two_items() -> Result<()> {
         let value = vec![1, 2];
-        verify_that!(value, superset_of([1, 2]))
+        verify_that!(value, superset_of([&1, &2]))
     }
 
     #[test]
     fn superset_of_matches_vec_when_actual_has_excess_element() -> Result<()> {
         let value = vec![1, 2, 3];
-        verify_that!(value, superset_of([1, 2]))
+        verify_that!(value, superset_of([&1, &2]))
     }
 
     #[test]
     fn superset_of_matches_vec_when_actual_has_excess_element_first() -> Result<()> {
         let value = vec![3, 1, 2];
-        verify_that!(value, superset_of([1, 2]))
+        verify_that!(value, superset_of([&1, &2]))
     }
 
     #[test]
     fn superset_of_matches_slice_with_one_element() -> Result<()> {
         let value = &[1];
-        verify_that!(*value, superset_of([1]))
+        verify_that!(value, superset_of([&1]))
     }
 
     #[test]
     fn superset_of_matches_hash_set_with_one_element() -> Result<()> {
         let value: HashSet<i32> = [1].into();
-        verify_that!(value, superset_of([1]))
+        verify_that!(value, superset_of([&1]))
     }
 
     #[test]
     fn superset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
         let value = vec![0];
-        verify_that!(value, not(superset_of([1])))
+        verify_that!(value, not(superset_of([&1])))
     }
 
     #[test]
     fn superset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
         let value = vec![2];
-        verify_that!(value, not(superset_of([2, 0])))
+        verify_that!(value, not(superset_of([&2, &0])))
     }
 
     #[test]
     fn superset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
-        let result = verify_that!(vec![0, 2, 3], superset_of([1, 2, 3]));
+        let result = verify_that!(vec![0, 2, 3], superset_of([&1, &2, &3]));
 
         verify_that!(
             result,
@@ -232,7 +207,7 @@
 
     #[test]
     fn superset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
-        let result = verify_that!(vec![1, 0, 3], superset_of([1, 2, 3]));
+        let result = verify_that!(vec![1, 0, 3], superset_of([&1, &2, &3]));
 
         verify_that!(
             result,
@@ -253,7 +228,7 @@
 
     #[test]
     fn superset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
-        let result = verify_that!(vec![0, 0, 3], superset_of([1, 2, 3]));
+        let result = verify_that!(vec![0, 0, 3], superset_of([&1, &2, &3]));
 
         verify_that!(
             result,
diff --git a/crates/googletest/src/matchers/tuple_matcher.rs b/crates/googletest/src/matchers/tuple_matcher.rs
index af55cbf..4822d52 100644
--- a/crates/googletest/src/matchers/tuple_matcher.rs
+++ b/crates/googletest/src/matchers/tuple_matcher.rs
@@ -23,16 +23,16 @@
 pub mod internal {
     use crate::{
         description::Description,
-        matcher::{Matcher, MatcherResult},
+        matcher::{Matcher, MatcherBase, MatcherResult},
     };
     use std::fmt::Debug;
 
+    impl MatcherBase for () {}
+
     // This implementation is provided for completeness, but is completely trivial.
     // The only actual value which can be supplied is (), which must match.
-    impl Matcher for () {
-        type ActualT = ();
-
-        fn matches(&self, _: &Self::ActualT) -> MatcherResult {
+    impl Matcher<()> for () {
+        fn matches(&self, _: ()) -> MatcherResult {
             MatcherResult::Match
         }
 
@@ -44,18 +44,75 @@
         }
     }
 
+    impl Matcher<&()> for () {
+        fn matches(&self, _: &()) -> MatcherResult {
+            MatcherResult::Match
+        }
+
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
+            <Self as Matcher<()>>::describe(self, matcher_result)
+        }
+    }
+
     /// Generates a tuple matcher for tuples of a specific length.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
     macro_rules! tuple_matcher_n {
         ($([$field_number:tt, $matcher_type:ident, $field_type:ident]),*) => {
-            impl<$($field_type: Debug, $matcher_type: Matcher<ActualT = $field_type>),*>
-                Matcher for ($($matcher_type,)*)
-            {
-                type ActualT = ($($field_type,)*);
+            impl<$($matcher_type: MatcherBase),*> MatcherBase for ($($matcher_type,)*){}
 
-                fn matches(&self, actual: &($($field_type,)*)) -> MatcherResult {
+            impl<$($field_type: Debug + Copy, $matcher_type: Matcher<$field_type>),*>
+                Matcher<($($field_type,)*)> for ($($matcher_type,)*)
+            {
+                fn matches(&self, actual:  ($($field_type,)*)) -> MatcherResult {
+                    $(match self.$field_number.matches(actual.$field_number) {
+                        MatcherResult::Match => {},
+                        MatcherResult::NoMatch => {
+                            return MatcherResult::NoMatch;
+                        }
+                    })*
+                    MatcherResult::Match
+                }
+
+                fn explain_match(&self, actual:  ($($field_type,)*)) -> Description  {
+                    let mut explanation = Description::new().text("which").nested(
+                        self.describe(self.matches(actual)));
+                    $(match self.$field_number.matches(actual.$field_number) {
+                        MatcherResult::Match => {},
+                        MatcherResult::NoMatch => {
+                            explanation = explanation
+                                .text(format!(concat!("Element #", $field_number, " is {:?},"),
+                                    actual.$field_number))
+                                .nested(self.$field_number.explain_match(actual.$field_number));
+                        }
+                    })*
+                    explanation
+                }
+
+                fn describe(&self, matcher_result: MatcherResult) -> Description {
+                    match matcher_result {
+                        MatcherResult::Match => {
+                            let mut description = Description::new().text(
+                                "is a tuple whose values respectively match:");
+                            $(description = description.nested(
+                                self.$field_number.describe(matcher_result));)*
+                            description
+                        }
+                        MatcherResult::NoMatch => {
+                            let mut description = Description::new().text(
+                                "is a tuple whose values do not respectively match:");
+                            $(description = description.nested(
+                                self.$field_number.describe(MatcherResult::Match));)*
+                            description
+                        }
+                    }
+                }
+            }
+            impl<'a, $($field_type: Debug, $matcher_type: Matcher<&'a $field_type>),*>
+                Matcher<&'a ($($field_type,)*)> for ($($matcher_type,)*)
+            {
+                fn matches(&self, actual:  &'a ($($field_type,)*)) -> MatcherResult {
                     $(match self.$field_number.matches(&actual.$field_number) {
                         MatcherResult::Match => {},
                         MatcherResult::NoMatch => {
@@ -65,13 +122,22 @@
                     MatcherResult::Match
                 }
 
-                fn explain_match(&self, actual: &($($field_type,)*)) -> Description {
-                    let mut explanation = Description::new().text("which").nested(self.describe(self.matches(actual)));
+                fn explain_match(&self, actual:  &'a ($($field_type,)*)) -> Description  {
+                    let mut explanation = Description::new()
+                        .text("which")
+                        .nested(
+                            Matcher::<&'a ($($field_type,)*)>::describe(
+                                self, self.matches(actual)));
                     $(match self.$field_number.matches(&actual.$field_number) {
                         MatcherResult::Match => {},
                         MatcherResult::NoMatch => {
                             explanation = explanation
-                                .text(format!(concat!("Element #", $field_number, " is {:?},"), actual.$field_number))
+                                .text(format!(
+                                    concat!(
+                                        "Element #",
+                                        $field_number,
+                                        " is {:?},"),
+                                        actual.$field_number))
                                 .nested(self.$field_number.explain_match(&actual.$field_number));
                         }
                     })*
@@ -81,13 +147,17 @@
                 fn describe(&self, matcher_result: MatcherResult) -> Description {
                     match matcher_result {
                         MatcherResult::Match => {
-                            let mut description = Description::new().text("is a tuple whose values respectively match:");
-                            $(description = description.nested(self.$field_number.describe(matcher_result));)*
+                            let mut description = Description::new().text(
+                                "is a tuple whose values respectively match:");
+                            $(description = description.nested(
+                                self.$field_number.describe(matcher_result));)*
                             description
                         }
                         MatcherResult::NoMatch => {
-                            let mut description = Description::new().text("is a tuple whose values do not respectively match:");
-                            $(description = description.nested(self.$field_number.describe(MatcherResult::Match));)*
+                            let mut description = Description::new().text(
+                                "is a tuple whose values do not respectively match:");
+                            $(description = description.nested(
+                                self.$field_number.describe(MatcherResult::Match));)*
                             description
                         }
                     }
diff --git a/crates/googletest/src/matchers/unordered_elements_are_matcher.rs b/crates/googletest/src/matchers/unordered_elements_are_matcher.rs
index f4585a4..50951d7 100644
--- a/crates/googletest/src/matchers/unordered_elements_are_matcher.rs
+++ b/crates/googletest/src/matchers/unordered_elements_are_matcher.rs
@@ -22,19 +22,19 @@
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(1), ge(2), anything()])?;   // Passes
+/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&2), anything()])?;   // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail_1() -> Result<()> {
-/// verify_that!(vec![1], unordered_elements_are![eq(1), ge(2)])?;              // Fails: container has wrong size
+/// verify_that!(vec![1], unordered_elements_are![eq(&1), ge(&2)])?;              // Fails: container has wrong size
 /// #     Ok(())
 /// # }
 /// # fn should_fail_2() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(1), ge(4), eq(2)])?; // Fails: second matcher not matched
+/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&4), eq(&2)])?; // Fails: second matcher not matched
 /// #     Ok(())
 /// # }
 /// # fn should_fail_3() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], unordered_elements_are![ge(3), ge(3), ge(3)])?; // Fails: no 1:1 correspondence
+/// verify_that!(vec![3, 2, 1], unordered_elements_are![ge(&3), ge(&3), ge(&3)])?; // Fails: no 1:1 correspondence
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -43,32 +43,15 @@
 /// # should_fail_3().unwrap_err();
 /// ```
 ///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
-///
-/// This can also match against [`HashMap`][std::collections::HashMap] and
-/// similar collections. The arguments are a sequence of pairs of matchers
-/// corresponding to the keys and their respective values.
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # use std::collections::HashMap;
-/// let value: HashMap<u32, &'static str> =
-///     HashMap::from_iter([(1, "One"), (2, "Two"), (3, "Three")]);
-/// verify_that!(
-///     value,
-///     unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
-/// )
-/// #     .unwrap();
-/// ```
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// slice. More precisely, the actual value must implement [`IntoIterator`].
 ///
 /// This can also be omitted in [`verify_that!`] macros and replaced with curly
 /// brackets.
 ///
 /// ```
 /// # use googletest::prelude::*;
-///  verify_that!(vec![1, 2], {eq(2), eq(1)})
+///  verify_that!(vec![1, 2], {eq(&2), eq(&1)})
 /// #     .unwrap();
 /// ```
 ///
@@ -86,12 +69,18 @@
 /// ```
 /// # use googletest::prelude::*;
 /// verify_that!(vec![vec![1,2], vec![3]],
-///   {unordered_elements_are![eq(2), eq(1)], unordered_elements_are![eq(3)]})
+///   {unordered_elements_are![eq(&2), eq(&1)], unordered_elements_are![eq(&3)]})
 /// # .unwrap();
 /// ```
 ///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
+///  If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(vec![1,2,3], unordered_elements_are![lt(&2), gt(&1), &3])
+/// #     .unwrap();
+/// ```
 ///
 /// The matcher proceeds in three stages:
 ///
@@ -119,29 +108,23 @@
 #[doc(hidden)]
 macro_rules! __unordered_elements_are {
     ($(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsAreMatcher, Requirements
-        };
-        UnorderedElementsAreMatcher::new([], Requirements::PerfectMatch)
-    }};
-
-    // TODO: Consider an alternative map-like syntax here similar to that used in
-    // https://crates.io/crates/maplit.
-    ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsOfMapAreMatcher, Requirements
-        };
-        UnorderedElementsOfMapAreMatcher::new(
-            [$((Box::new($key_matcher), Box::new($value_matcher))),*],
-            Requirements::PerfectMatch
-        )
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::
+        UnorderedElementsAreMatcher::new(
+            [],
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::
+            Requirements::PerfectMatch)
     }};
 
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsAreMatcher, Requirements
-        };
-        UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::PerfectMatch)
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::
+        UnorderedElementsAreMatcher::new(
+            [$(Box::new(
+                $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+                    $matcher
+                )
+            )),*],
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::
+            Requirements::PerfectMatch)
     }};
 }
 
@@ -160,20 +143,20 @@
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], contains_each![eq(2), ge(3)])?;   // Passes
-/// verify_that!(vec![3, 2, 1], contains_each![ge(2), ge(2)])?;   // Passes
+/// verify_that!(vec![3, 2, 1], contains_each![eq(&2), ge(&3)])?;   // Passes
+/// verify_that!(vec![3, 2, 1], contains_each![ge(&2), ge(&2)])?;   // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail_1() -> Result<()> {
-/// verify_that!(vec![1], contains_each![eq(1), ge(2)])?;         // Fails: container too small
+/// verify_that!(vec![1], contains_each![eq(&1), ge(&2)])?;         // Fails: container too small
 /// #     Ok(())
 /// # }
 /// # fn should_fail_2() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], contains_each![eq(1), ge(4)])?;   // Fails: second matcher unmatched
+/// verify_that!(vec![3, 2, 1], contains_each![eq(&1), ge(&4)])?;   // Fails: second matcher unmatched
 /// #     Ok(())
 /// # }
 /// # fn should_fail_3() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], contains_each![ge(3), ge(3), ge(3)])?; // Fails: no matching
+/// verify_that!(vec![3, 2, 1], contains_each![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -182,26 +165,18 @@
 /// # should_fail_3().unwrap_err();
 /// ```
 ///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// slice. More precisely, the actual value must implement [`IntoIterator`].
 ///
-/// This can also match against [`HashMap`][std::collections::HashMap] and
-/// similar collections. The arguments are a sequence of pairs of matchers
-/// corresponding to the keys and their respective values.
+///  If an inner matcher is `eq(...)`, it can be omitted:
 ///
 /// ```
 /// # use googletest::prelude::*;
-/// # use std::collections::HashMap;
-/// let value: HashMap<u32, &'static str> =
-///     HashMap::from_iter([(1, "One"), (2, "Two"), (3, "Three")]);
-/// verify_that!(value, contains_each![(eq(2), eq("Two")), (eq(1), eq("One"))])
+///
+/// verify_that!(vec![1,2,3], contains_each![lt(&2), &3])
 /// #     .unwrap();
 /// ```
 ///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
-///
 /// The matcher proceeds in three stages:
 ///
 /// 1. It first checks whether the actual value is large enough to possibly be
@@ -225,29 +200,21 @@
 #[doc(hidden)]
 macro_rules! __contains_each {
     ($(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsAreMatcher, Requirements
-        };
-        UnorderedElementsAreMatcher::new([], Requirements::Superset)
-    }};
-
-    // TODO: Consider an alternative map-like syntax here similar to that used in
-    // https://crates.io/crates/maplit.
-    ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsOfMapAreMatcher, Requirements
-        };
-        UnorderedElementsOfMapAreMatcher::new(
-            [$((Box::new($key_matcher), Box::new($value_matcher))),*],
-            Requirements::Superset
-        )
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::
+        UnorderedElementsAreMatcher::new(
+            [],
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
     }};
 
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsAreMatcher, Requirements
-        };
-        UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::Superset)
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::
+        UnorderedElementsAreMatcher::new(
+            [$(Box::new(
+                $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+                    $matcher
+                )
+            )),*],
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
     }}
 }
 
@@ -267,20 +234,20 @@
 /// ```
 /// # use googletest::prelude::*;
 /// # fn should_pass() -> Result<()> {
-/// verify_that!(vec![2, 1], is_contained_in![eq(1), ge(2)])?;   // Passes
-/// verify_that!(vec![2, 1], is_contained_in![ge(1), ge(1)])?;   // Passes
+/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&2)])?;   // Passes
+/// verify_that!(vec![2, 1], is_contained_in![ge(&1), ge(&1)])?;   // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail_1() -> Result<()> {
-/// verify_that!(vec![1, 2, 3], is_contained_in![eq(1), ge(2)])?; // Fails: container too large
+/// verify_that!(vec![1, 2, 3], is_contained_in![eq(&1), ge(&2)])?; // Fails: container too large
 /// #     Ok(())
 /// # }
 /// # fn should_fail_2() -> Result<()> {
-/// verify_that!(vec![2, 1], is_contained_in![eq(1), ge(4)])?;    // Fails: second matcher unmatched
+/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&4)])?;    // Fails: second matcher unmatched
 /// #     Ok(())
 /// # }
 /// # fn should_fail_3() -> Result<()> {
-/// verify_that!(vec![3, 1], is_contained_in![ge(3), ge(3), ge(3)])?; // Fails: no matching
+/// verify_that!(vec![3, 1], is_contained_in![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
 /// #     Ok(())
 /// # }
 /// # should_pass().unwrap();
@@ -289,28 +256,18 @@
 /// # should_fail_3().unwrap_err();
 /// ```
 ///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a slice.
+/// More precisely, the actual value must implement [`IntoIterator`].
 ///
-/// This can also match against [`HashMap`][std::collections::HashMap] and
-/// similar collections. The arguments are a sequence of pairs of matchers
-/// corresponding to the keys and their respective values.
+///  If an inner matcher is `eq(...)`, it can be omitted:
 ///
 /// ```
 /// # use googletest::prelude::*;
-/// # use std::collections::HashMap;
-/// let value: HashMap<u32, &'static str> = HashMap::from_iter([(1, "One"), (2, "Two")]);
-/// verify_that!(
-///     value,
-///     is_contained_in![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
-/// )
+///
+/// verify_that!(vec![1,2,3], is_contained_in![lt(&2), &3, &4, gt(&0)])
 /// #     .unwrap();
 /// ```
 ///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
-///
 /// The matcher proceeds in three stages:
 ///
 /// 1. It first checks whether the actual value is too large to possibly be
@@ -334,29 +291,20 @@
 #[doc(hidden)]
 macro_rules! __is_contained_in {
     ($(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsAreMatcher, Requirements
-        };
-        UnorderedElementsAreMatcher::new([], Requirements::Subset)
-    }};
-
-    // TODO: Consider an alternative map-like syntax here similar to that used in
-    // https://crates.io/crates/maplit.
-    ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsOfMapAreMatcher, Requirements
-        };
-        UnorderedElementsOfMapAreMatcher::new(
-            [$((Box::new($key_matcher), Box::new($value_matcher))),*],
-            Requirements::Subset
-        )
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::
+        UnorderedElementsAreMatcher::new(
+            [], $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
     }};
 
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
-            UnorderedElementsAreMatcher, Requirements
-        };
-        UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::Subset)
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::
+        UnorderedElementsAreMatcher::new(
+            [$(Box::new(
+                $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+                    $matcher
+                )
+            )),*],
+            $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
     }}
 }
 
@@ -366,31 +314,25 @@
 #[doc(hidden)]
 pub mod internal {
     use crate::description::Description;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::{Matcher, MatcherBase, MatcherResult};
     use crate::matcher_support::count_elements::count_elements;
     use std::collections::HashSet;
     use std::fmt::{Debug, Display};
-    use std::marker::PhantomData;
 
     /// This struct is meant to be used only through the
     /// `unordered_elements_are![...]` macro.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
-    pub struct UnorderedElementsAreMatcher<'a, ContainerT: ?Sized, T: Debug, const N: usize> {
-        elements: [Box<dyn Matcher<ActualT = T> + 'a>; N],
+    #[derive(MatcherBase)]
+    pub struct UnorderedElementsAreMatcher<'a, T: Debug + Copy, const N: usize> {
+        elements: [Box<dyn Matcher<T> + 'a>; N],
         requirements: Requirements,
-        phantom: PhantomData<ContainerT>,
     }
 
-    impl<'a, ContainerT: ?Sized, T: Debug, const N: usize>
-        UnorderedElementsAreMatcher<'a, ContainerT, T, N>
-    {
-        pub fn new(
-            elements: [Box<dyn Matcher<ActualT = T> + 'a>; N],
-            requirements: Requirements,
-        ) -> Self {
-            Self { elements, requirements, phantom: Default::default() }
+    impl<'a, T: Debug + Copy, const N: usize> UnorderedElementsAreMatcher<'a, T, N> {
+        pub fn new(elements: [Box<dyn Matcher<T> + 'a>; N], requirements: Requirements) -> Self {
+            Self { elements, requirements }
         }
     }
 
@@ -403,19 +345,17 @@
     // least one expected element and vice versa.
     // 3. `UnorderedElementsAreMatcher` verifies that a perfect matching exists
     // using Ford-Fulkerson.
-    impl<'a, T: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher
-        for UnorderedElementsAreMatcher<'a, ContainerT, T, N>
+    impl<'a, T: Debug + Copy, ContainerT: Debug + Copy, const N: usize> Matcher<ContainerT>
+        for UnorderedElementsAreMatcher<'a, T, N>
     where
-        for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
+        ContainerT: IntoIterator<Item = T>,
     {
-        type ActualT = ContainerT;
-
-        fn matches(&self, actual: &ContainerT) -> MatcherResult {
+        fn matches(&self, actual: ContainerT) -> MatcherResult {
             let match_matrix = MatchMatrix::generate(actual, &self.elements);
             match_matrix.is_match_for(self.requirements).into()
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> Description {
+        fn explain_match(&self, actual: ContainerT) -> Description {
             if let Some(size_mismatch_explanation) =
                 self.requirements.explain_size_mismatch(actual, N)
             {
@@ -450,89 +390,8 @@
         }
     }
 
-    type KeyValueMatcher<'a, KeyT, ValueT> =
-        (Box<dyn Matcher<ActualT = KeyT> + 'a>, Box<dyn Matcher<ActualT = ValueT> + 'a>);
-
-    /// This is the analogue to [UnorderedElementsAreMatcher] for maps and
-    /// map-like collections.
-    ///
-    /// **For internal use only. API stablility is not guaranteed!**
-    #[doc(hidden)]
-    pub struct UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, const N: usize>
-    where
-        ContainerT: ?Sized,
-        KeyT: Debug,
-        ValueT: Debug,
-    {
-        elements: [KeyValueMatcher<'a, KeyT, ValueT>; N],
-        requirements: Requirements,
-        phantom: PhantomData<ContainerT>,
-    }
-
-    impl<'a, ContainerT, KeyT: Debug, ValueT: Debug, const N: usize>
-        UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, N>
-    {
-        pub fn new(
-            elements: [KeyValueMatcher<'a, KeyT, ValueT>; N],
-            requirements: Requirements,
-        ) -> Self {
-            Self { elements, requirements, phantom: Default::default() }
-        }
-    }
-
-    impl<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher
-        for UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, N>
-    where
-        for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
-    {
-        type ActualT = ContainerT;
-
-        fn matches(&self, actual: &ContainerT) -> MatcherResult {
-            let match_matrix = MatchMatrix::generate_for_map(actual, &self.elements);
-            match_matrix.is_match_for(self.requirements).into()
-        }
-
-        fn explain_match(&self, actual: &ContainerT) -> Description {
-            if let Some(size_mismatch_explanation) =
-                self.requirements.explain_size_mismatch(actual, N)
-            {
-                return size_mismatch_explanation;
-            }
-
-            let match_matrix = MatchMatrix::generate_for_map(actual, &self.elements);
-            if let Some(unmatchable_explanation) =
-                match_matrix.explain_unmatchable(self.requirements)
-            {
-                return unmatchable_explanation;
-            }
-
-            let best_match = match_matrix.find_best_match();
-
-            best_match
-                .get_explanation_for_map(actual, &self.elements, self.requirements)
-                .unwrap_or("whose elements all match".into())
-        }
-
-        fn describe(&self, matcher_result: MatcherResult) -> Description {
-            format!(
-                "{} elements matching in any order:\n{}",
-                if matcher_result.into() { "contains" } else { "doesn't contain" },
-                self.elements
-                    .iter()
-                    .map(|(key_matcher, value_matcher)| format!(
-                        "{} => {}",
-                        key_matcher.describe(MatcherResult::Match),
-                        value_matcher.describe(MatcherResult::Match)
-                    ))
-                    .collect::<Description>()
-                    .indent()
-            )
-            .into()
-        }
-    }
-
     /// The requirements of the mapping between matchers and actual values by
-    /// which [`UnorderedElemetnsAre`] is deemed to match its input.
+    /// which [`UnorderedElementsAre`] is deemed to match its input.
     ///
     /// **For internal use only. API stablility is not guaranteed!**
     #[doc(hidden)]
@@ -552,14 +411,11 @@
     }
 
     impl Requirements {
-        fn explain_size_mismatch<ContainerT: ?Sized>(
+        fn explain_size_mismatch<ContainerT: IntoIterator + Copy>(
             &self,
-            actual: &ContainerT,
+            actual: ContainerT,
             expected_size: usize,
-        ) -> Option<Description>
-        where
-            for<'b> &'b ContainerT: IntoIterator,
-        {
+        ) -> Option<Description> {
             let actual_size = count_elements(actual);
             match self {
                 Requirements::PerfectMatch if actual_size != expected_size => Some(
@@ -601,13 +457,10 @@
     struct MatchMatrix<const N: usize>(Vec<[MatcherResult; N]>);
 
     impl<const N: usize> MatchMatrix<N> {
-        fn generate<'a, T: Debug + 'a, ContainerT: Debug + ?Sized>(
-            actual: &ContainerT,
-            expected: &[Box<dyn Matcher<ActualT = T> + 'a>; N],
-        ) -> Self
-        where
-            for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
-        {
+        fn generate<'a, T: Debug + Copy + 'a, ContainerT: Debug + Copy + IntoIterator<Item = T>>(
+            actual: ContainerT,
+            expected: &[Box<dyn Matcher<T> + 'a>; N],
+        ) -> Self {
             let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]);
             for (actual_idx, actual) in actual.into_iter().enumerate() {
                 for (expected_idx, expected) in expected.iter().enumerate() {
@@ -617,24 +470,6 @@
             matrix
         }
 
-        fn generate_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>(
-            actual: &ContainerT,
-            expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N],
-        ) -> Self
-        where
-            for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
-        {
-            let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]);
-            for (actual_idx, (actual_key, actual_value)) in actual.into_iter().enumerate() {
-                for (expected_idx, (expected_key, expected_value)) in expected.iter().enumerate() {
-                    matrix.0[actual_idx][expected_idx] = (expected_key.matches(actual_key).into()
-                        && expected_value.matches(actual_value).into())
-                    .into();
-                }
-            }
-            matrix
-        }
-
         fn is_match_for(&self, requirements: Requirements) -> bool {
             match requirements {
                 Requirements::PerfectMatch => {
@@ -959,15 +794,16 @@
             (0..N).filter(|expected_idx| !matched_expected.contains(expected_idx)).collect()
         }
 
-        fn get_explanation<'a, T: Debug, ContainerT: Debug + ?Sized>(
+        fn get_explanation<
+            'a,
+            T: Debug + Copy,
+            ContainerT: Debug + Copy + IntoIterator<Item = T>,
+        >(
             &self,
-            actual: &ContainerT,
-            expected: &[Box<dyn Matcher<ActualT = T> + 'a>; N],
+            actual: ContainerT,
+            expected: &[Box<dyn Matcher<T> + 'a>; N],
             requirements: Requirements,
-        ) -> Option<Description>
-        where
-            for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
-        {
+        ) -> Option<Description> {
             let actual: Vec<_> = actual.into_iter().collect();
             if self.is_full_match() {
                 return None;
@@ -1005,71 +841,12 @@
                 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
             ).into())
         }
-
-        fn get_explanation_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>(
-            &self,
-            actual: &ContainerT,
-            expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N],
-            requirements: Requirements,
-        ) -> Option<Description>
-        where
-            for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
-        {
-            let actual: Vec<_> = actual.into_iter().collect();
-            if self.is_full_match() {
-                return None;
-            }
-            let mut error_message =
-                format!("which does not have a {requirements} match with the expected elements.");
-
-            error_message.push_str("\n  The best match found was: ");
-
-            let matches = self.get_matches()
-                .map(|(actual_idx, expected_idx)| {
-                    format!(
-                        "Actual element {:?} => {:?} at index {actual_idx} matched expected element `{}` => `{}` at index {expected_idx}.",
-                        actual[actual_idx].0,
-                        actual[actual_idx].1,
-                        expected[expected_idx].0.describe(MatcherResult::Match),
-                        expected[expected_idx].1.describe(MatcherResult::Match),
-                    )
-                });
-
-            let unmatched_actual = self.get_unmatched_actual()
-                .map(|actual_idx| {
-                    format!(
-                        "Actual element {:#?} => {:#?} at index {actual_idx} did not match any remaining expected element.",
-                        actual[actual_idx].0,
-                        actual[actual_idx].1,
-                    )
-                });
-
-            let unmatched_expected = self.get_unmatched_expected()
-                .into_iter()
-                .map(|expected_idx| {
-                    format!(
-                        "Expected element `{}` => `{}` at index {expected_idx} did not match any remaining actual element.",
-                        expected[expected_idx].0.describe(MatcherResult::Match),
-                        expected[expected_idx].1.describe(MatcherResult::Match),
-                    )
-                });
-
-            let best_match = matches
-                .chain(unmatched_actual)
-                .chain(unmatched_expected)
-                .collect::<Description>()
-                .indent();
-            Some(format!(
-                "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
-            ).into())
-        }
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use super::internal::UnorderedElementsOfMapAreMatcher;
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::matcher::MatcherResult;
     use crate::prelude::*;
     use indoc::indoc;
     use std::collections::HashMap;
@@ -1081,20 +858,26 @@
         // compiler takes care of that, but when the matcher is created separately,
         // we must create the constitute matchers separately so that they
         // aren't dropped too early.
-        let matchers = ((eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")));
-        let matcher: UnorderedElementsOfMapAreMatcher<HashMap<i32, &str>, _, _, 3> = unordered_elements_are![
-            (matchers.0.0, matchers.0.1),
-            (matchers.1.0, matchers.1.1),
-            (matchers.2.0, matchers.2.1)
+        let matchers = ((eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")));
+        let matcher = unordered_elements_are![
+            (matchers.0 .0, matchers.0 .1),
+            (matchers.1 .0, matchers.1 .1),
+            (matchers.2 .0, matchers.2 .1)
         ];
         verify_that!(
-            Matcher::describe(&matcher, MatcherResult::Match),
+            Matcher::<&HashMap<i32, String>>::describe(&matcher, MatcherResult::Match),
             displays_as(eq(indoc!(
                 "
                 contains elements matching in any order:
-                  is equal to 2 => is equal to \"Two\"
-                  is equal to 1 => is equal to \"One\"
-                  is equal to 3 => is equal to \"Three\""
+                  0. is a tuple whose values respectively match:
+                       is equal to 2
+                       is equal to \"Two\"
+                  1. is a tuple whose values respectively match:
+                       is equal to 1
+                       is equal to \"One\"
+                  2. is a tuple whose values respectively match:
+                       is equal to 3
+                       is equal to \"Three\""
             )))
         )
     }
@@ -1106,22 +889,26 @@
         // compiler takes care of that, but when the matcher is created separately,
         // we must create the constitute matchers separately so that they
         // aren't dropped too early.
-        let matchers = ((anything(), eq(1)), (anything(), eq(2)), (anything(), eq(2)));
-        let matcher: UnorderedElementsOfMapAreMatcher<HashMap<u32, u32>, _, _, 3> = unordered_elements_are![
-            (matchers.0.0, matchers.0.1),
-            (matchers.1.0, matchers.1.1),
-            (matchers.2.0, matchers.2.1),
-        ];
         let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
+        let matchers = ((anything(), eq(&1)), (anything(), eq(&2)), (anything(), eq(&2)));
+        let matcher = unordered_elements_are![
+            (matchers.0 .0, matchers.0 .1),
+            (matchers.1 .0, matchers.1 .1),
+            (matchers.2 .0, matchers.2 .1),
+        ];
         verify_that!(
             matcher.explain_match(&value),
-            displays_as(contains_regex(
-                "Actual element 2 => 2 at index [0-2] matched expected element `is anything` => `is equal to 2` at index [0-2]."
-            )).and(displays_as(contains_regex(
-                "Actual element [0-1] => [0-1] at index [0-2] did not match any remaining expected element."
-            ))).and(displays_as(contains_substring(
-                "Expected element `is anything` => `is equal to 2` at index 2 did not match any remaining actual element."
-            )))
+            all![
+                displays_as(contains_regex(
+                    "Actual element \\(2, 2\\) at index [0-2] matched expected element `is a tuple whose values respectively match:\n    is anything\n    is equal to 2` at index [0-2]."
+                )),
+                displays_as(contains_regex(
+                    "Actual element \\(\n      [0-1],\n      [0-1],\n  \\) at index [0-2] did not match any remaining expected element."
+                )),
+                displays_as(contains_substring(
+                    "Expected element `is a tuple whose values respectively match:\n    is anything\n    is equal to 2` at index 2 did not match any remaining actual element."
+                ))
+            ]
         )
     }
 }
diff --git a/crates/googletest/tests/all_matcher_test.rs b/crates/googletest/tests/all_matcher_test.rs
index 8b36fc0..03f645a 100644
--- a/crates/googletest/tests/all_matcher_test.rs
+++ b/crates/googletest/tests/all_matcher_test.rs
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::Matcher;
 use googletest::prelude::*;
 use indoc::indoc;
 
@@ -54,7 +53,7 @@
     #[derive(Debug, PartialEq)]
     struct AStruct(i32);
     let expected_value = AStruct(123);
-    verify_that!(AStruct(123), all![eq_deref_of(&expected_value)])
+    verify_that!(AStruct(123), all![eq(&expected_value)])
 }
 
 #[test]
@@ -115,7 +114,7 @@
 #[test]
 fn formats_error_message_correctly_when_all_is_inside_ok() -> Result<()> {
     let value: std::result::Result<i32, std::io::Error> = Ok(4);
-    let result = verify_that!(value, ok(all![eq(1), eq(2), eq(3)]));
+    let result = verify_that!(value, ok(all![eq(&1), eq(&2), eq(&3)]));
     verify_that!(
         result,
         err(displays_as(contains_substring(indoc!(
diff --git a/crates/googletest/tests/any_matcher_test.rs b/crates/googletest/tests/any_matcher_test.rs
index 82ed046..425e99c 100644
--- a/crates/googletest/tests/any_matcher_test.rs
+++ b/crates/googletest/tests/any_matcher_test.rs
@@ -12,16 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::Matcher;
 use googletest::prelude::*;
 use indoc::indoc;
 
 #[test]
-fn does_not_match_value_when_list_is_empty() -> Result<()> {
-    verify_that!((), not(any!()))
-}
-
-#[test]
 fn matches_value_with_single_matching_component() -> Result<()> {
     verify_that!(123, any!(eq(123)))
 }
@@ -54,7 +48,7 @@
     #[derive(Debug, PartialEq)]
     struct AStruct(i32);
     let expected_value = AStruct(123);
-    verify_that!(AStruct(123), any![eq_deref_of(&expected_value)])
+    verify_that!(AStruct(123), any![eq(&expected_value)])
 }
 
 #[test]
@@ -66,11 +60,6 @@
 }
 
 #[test]
-fn mismatch_description_empty_matcher() -> Result<()> {
-    verify_that!(any!().explain_match("Three"), displays_as(eq("which never matches")))
-}
-
-#[test]
 fn all_multiple_failed_assertions() -> Result<()> {
     let result = verify_that!(4, any![eq(1), eq(2), eq(3)]);
     verify_that!(
@@ -115,7 +104,7 @@
 #[test]
 fn formats_error_message_correctly_when_any_is_inside_ok() -> Result<()> {
     let value: std::result::Result<i32, std::io::Error> = Ok(4);
-    let result = verify_that!(value, ok(any![eq(1), eq(2), eq(3)]));
+    let result = verify_that!(value, ok(any![eq(&1), eq(&2), eq(&3)]));
     verify_that!(
         result,
         err(displays_as(contains_substring(indoc!(
diff --git a/crates/googletest/tests/assertions_test.rs b/crates/googletest/tests/assertions_test.rs
new file mode 100644
index 0000000..dee073d
--- /dev/null
+++ b/crates/googletest/tests/assertions_test.rs
@@ -0,0 +1,427 @@
+mod verify_pred {
+    use googletest::prelude::*;
+    use indoc::indoc;
+
+    #[test]
+    fn supports_function_call_with_non_debug_types() -> Result<()> {
+        // Non-Debug - cannot be printed.
+        struct Apple;
+        fn f(_a: &Apple, _b: u32, _c: u32) -> bool {
+            false
+        }
+        fn g(_a: u32) -> u32 {
+            5
+        }
+
+        let a = &Apple;
+        let res = verify_pred!(f(a, g(g(3)), 1 + 2));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                f(a, g(g(3)), 1 + 2) was false with
+                  a does not implement Debug,
+                  g(g(3)) = 5,
+                  1 + 2 = 3,
+                  at"
+            })))
+        )
+    }
+
+    #[test]
+    fn supports_trailing_comma() -> Result<()> {
+        verify_that!(verify_pred!(false,), err(anything()))
+    }
+
+    #[test]
+    fn supports_non_function() -> Result<()> {
+        verify_pred!(true)?;
+        verify_that!(verify_pred!(false), err(anything()))
+    }
+
+    #[test]
+    fn does_not_print_literals() -> Result<()> {
+        trait Foo {
+            fn f(&self, _a: u32, _b: i32, _c: u32, _d: &str) -> bool {
+                false
+            }
+        }
+        impl Foo for i32 {}
+
+        let res = verify_pred!(0.f(1, 2_i32.abs(), 1 + 2, "hello"));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {r#"
+                0.f(1, 2_i32.abs(), 1 + 2, "hello") was false with
+                  2_i32.abs() = 2,
+                  1 + 2 = 3,
+                  at"#
+            })))
+        )
+    }
+
+    #[test]
+    fn supports_chained_field_access_and_method_calls_with_non_debug_types() -> Result<()> {
+        // Non-Debug
+        struct Apple {
+            b: Banana,
+        }
+        #[derive(Debug)]
+        struct Banana;
+        impl Banana {
+            fn c(&self, _c: &Cherry, _d: u32) -> bool {
+                false
+            }
+        }
+        // Non-Debug - cannot be printed.
+        struct Cherry;
+
+        let a = Apple { b: Banana };
+        let c = &Cherry;
+        let d = 3;
+        let res = verify_pred!(a.b.c(c, d));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                a.b.c(c, d) was false with
+                  a does not implement Debug,
+                  a.b = Banana,
+                  c does not implement Debug,
+                  d = 3,
+                  at"
+            })))
+        )
+    }
+
+    #[test]
+    fn evaluates_functions_and_arguments_exactly_once() -> Result<()> {
+        let mut a = 0;
+        let mut foo = |_b: u32| {
+            a += 1;
+            false
+        };
+        let mut b = 0;
+        let mut bar = || {
+            b += 10;
+            b
+        };
+
+        let res = verify_pred!(foo(bar()));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                foo(bar()) was false with
+                  bar() = 10,
+                  at"
+            })))
+        )?;
+
+        verify_that!((a, b), eq((1, 10)))
+    }
+
+    #[test]
+    fn evaluates_methods_and_arguments_exactly_once() -> Result<()> {
+        struct Apple(u32);
+        impl Apple {
+            fn c(&mut self, _b: bool) -> bool {
+                self.0 += 1;
+                false
+            }
+        }
+        let mut a = Apple(0);
+        let mut b = Apple(10);
+
+        let res = verify_pred!(a.c(b.c(false)));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                a.c(b.c(false)) was false with
+                  a does not implement Debug,
+                  b.c(false) = false,
+                  at"
+            })))
+        )?;
+
+        verify_that!((a.0, b.0), eq((1, 11)))
+    }
+
+    #[test]
+    fn supports_chained_method_calls() -> Result<()> {
+        #[derive(Debug)]
+        struct Apple;
+        impl Apple {
+            fn b(&self, _b: u32) -> Banana {
+                Banana
+            }
+        }
+        // Non-Debug: not printed on error.
+        struct Banana;
+        impl Banana {
+            fn c(&self, _c0: u32, _c1: Cherry) -> bool {
+                false
+            }
+        }
+        // Non-Debug: not printed on error.
+        #[derive(Copy, Clone)]
+        struct Cherry;
+
+        let a = Apple;
+        let v = 10;
+        let res = verify_pred!(a.b(v).c(11, Cherry));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                a.b(v).c(11, Cherry) was false with
+                  a = Apple,
+                  v = 10,
+                  a.b(v) does not implement Debug,
+                  Cherry does not implement Debug,
+                  at"
+            })))
+        )
+    }
+
+    #[test]
+    fn prints_consumed_values() -> Result<()> {
+        // Non-Debug
+        struct Apple;
+        impl Apple {
+            fn b(self) -> Banana {
+                Banana
+            }
+        }
+        #[derive(Debug)]
+        struct Banana;
+        impl Banana {
+            fn c(self) -> bool {
+                false
+            }
+        }
+
+        let a = Apple;
+        let res = verify_pred!(a.b().c());
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                a.b().c() was false with
+                  a does not implement Debug,
+                  a.b() = Banana,
+                  at"
+            })))
+        )
+    }
+
+    #[test]
+    fn works_with_realistic_example_with_consumed_intermediate_values() -> Result<()> {
+        let res =
+            verify_pred!(vec![1, 2].into_iter().map(|x| x * 2).collect::<Vec<_>>().is_empty());
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                vec! [1, 2].into_iter().map(| x | x * 2).collect :: < Vec < _ > >
+                ().is_empty() was false with
+                  vec! [1, 2] = [1, 2],
+                  vec! [1, 2].into_iter() = IntoIter([1, 2]),
+                  | x | x * 2 does not implement Debug,
+                  vec! [1, 2].into_iter().map(| x | x * 2) = Map { iter: IntoIter([1, 2]) },
+                  vec! [1, 2].into_iter().map(| x | x * 2).collect :: < Vec < _ > > () = [2, 4],
+                  at"
+            })))
+        )
+    }
+
+    #[test]
+    fn values_should_be_accessible_after_test() -> Result<()> {
+        // Not `Copy` and should not be consumed by the generated test code.
+        #[derive(Debug)]
+        struct Apple;
+        impl Apple {
+            fn b(&self, _c: &mut u32) -> bool {
+                false
+            }
+        }
+
+        let mut c = 0;
+        let a = Apple;
+        let res = verify_pred!(a.b(&mut c));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                a.b(& mut c) was false with
+                  a = Apple,
+                  & mut c = 0,
+                  at"
+            })))
+        )?;
+
+        // `a` and `&mut c` should still be accessible after the test despite not being
+        // `Copy`.
+        let _ = a.b(&mut c);
+
+        Ok(())
+    }
+
+    #[test]
+    fn prints_values_for_mutating_expressions() -> Result<()> {
+        let mut a = 1;
+        let mut b = 2;
+        let mut c = 0;
+        trait Mutator {
+            fn mutate_and_false(&mut self, b: &mut u32) -> bool;
+        }
+        impl Mutator for u32 {
+            fn mutate_and_false(&mut self, b: &mut u32) -> bool {
+                *self += 10;
+                *b += 20;
+                false
+            }
+        }
+
+        // Macro to to avoid the inconsistency in how `;` and `&mut` are printed between
+        // Rust versions when printing out the stringified version of the block.
+        macro_rules! block_a {
+            () => {{
+                c += 10;
+                &mut a
+            }};
+        }
+        macro_rules! block_b {
+            () => {{
+                c += 100;
+                &mut b
+            }};
+        }
+        let res = verify_pred! { block_a!().mutate_and_false(block_b!()) };
+
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                block_a! ().mutate_and_false(block_b! ()) was false with
+                  block_a! () = 1,
+                  block_b! () = 2,
+                  at"
+            })))
+        )?;
+
+        verify_that!((a, b, c), eq((11, 22, 110)))
+    }
+
+    #[test]
+    fn values_can_be_insulated_with_parens() -> Result<()> {
+        // Not `Copy` and has a consuming method.
+        struct Apple;
+        impl Apple {
+            fn b(self) -> Banana {
+                Banana
+            }
+        }
+        #[derive(Debug)]
+        struct Banana;
+        impl Banana {
+            fn c(&self) -> bool {
+                false
+            }
+        }
+
+        let a = Apple;
+        let res = verify_pred!({ a.b() }.c());
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                { a.b() }.c() was false with
+                  { a.b() } = Banana,
+                  at"
+            })))
+        )
+    }
+
+    #[test]
+    fn binary_operator() -> Result<()> {
+        // Add chaining and function calls.
+        fn f(x: u32) -> u32 {
+            x + 1
+        }
+        #[derive(Debug)]
+        struct Apple;
+        impl Apple {
+            fn b(&self, y: u32) -> u32 {
+                y + 10
+            }
+        }
+        let a = Apple;
+        let x = 1;
+        let y = 2;
+        let res = verify_pred!(f(x) - 1 == a.b(y + 1) + f(y));
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                f(x) - 1 == a.b(y + 1) + f(y) was false with
+                  x = 1,
+                  f(x) = 2,
+                  f(x) - 1 = 1,
+                  a = Apple,
+                  y + 1 = 3,
+                  a.b(y + 1) = 13,
+                  y = 2,
+                  f(y) = 3,
+                  a.b(y + 1) + f(y) = 16,
+                  at"
+            })))
+        )
+    }
+
+    #[rustversion::before(1.77)]
+    #[test]
+    fn unary_operator() -> Result<()> {
+        #[derive(Debug)]
+        struct Apple;
+        impl Apple {
+            fn b(&self, _b: u32, _c: i32) -> i32 {
+                0
+            }
+        }
+
+        let a = Apple;
+        let b = 1;
+        let res = verify_pred!(!a.b(b, -1) == !-2);
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                ! a.b(b, - 1) ==! - 2 was false with
+                  a = Apple,
+                  b = 1,
+                  a.b(b, - 1) = 0,
+                  ! a.b(b, - 1) = -1,
+                  ! - 2 = 1,
+                  at"
+            })))
+        )
+    }
+
+    #[rustversion::since(1.77)]
+    #[test]
+    fn unary_operator() -> Result<()> {
+        #[derive(Debug)]
+        struct Apple;
+        impl Apple {
+            fn b(&self, _b: u32, _c: i32) -> i32 {
+                0
+            }
+        }
+
+        let a = Apple;
+        let b = 1;
+        let res = verify_pred!(!a.b(b, -1) == !-2);
+        verify_that!(
+            res,
+            err(displays_as(contains_substring(indoc! {"
+                ! a.b(b, - 1) == ! - 2 was false with
+                  a = Apple,
+                  b = 1,
+                  a.b(b, - 1) = 0,
+                  ! a.b(b, - 1) = -1,
+                  ! - 2 = 1,
+                  at"
+            })))
+        )
+    }
+}
diff --git a/crates/googletest/tests/colorized_diff_test.rs b/crates/googletest/tests/colorized_diff_test.rs
index d056020..2029521 100644
--- a/crates/googletest/tests/colorized_diff_test.rs
+++ b/crates/googletest/tests/colorized_diff_test.rs
@@ -32,7 +32,7 @@
     std::env::remove_var("NO_COLOR");
     std::env::set_var("FORCE_COLOR", "1");
 
-    let result = verify_that!(build_text(1..50), eq(build_text(1..51)));
+    let result = verify_that!(build_text(1..50), eq(&build_text(1..51)));
 
     verify_that!(
         result,
diff --git a/crates/googletest/tests/composition_test.rs b/crates/googletest/tests/composition_test.rs
index 7ae146f..e5f2cd7 100644
--- a/crates/googletest/tests/composition_test.rs
+++ b/crates/googletest/tests/composition_test.rs
@@ -17,14 +17,14 @@
 #[test]
 fn all_matcher_works_as_inner_matcher() -> Result<()> {
     let value = vec![1];
-    verify_that!(value, contains_each![all!(gt(0), lt(2))])
+    verify_that!(value, contains_each![all!(gt(&0), lt(&2))])
 }
 
 #[test]
 fn matches_pattern_works_as_inner_matcher() -> Result<()> {
     #[derive(Debug)]
     struct AStruct(i32);
-    verify_that!(vec![AStruct(123)], contains_each![matches_pattern!(AStruct(eq(123)))])
+    verify_that!(vec![AStruct(123)], contains_each![matches_pattern!(&AStruct(eq(123)))])
 }
 
 #[test]
@@ -38,7 +38,7 @@
     }
     verify_that!(
         vec![AStruct(123)],
-        contains_each![matches_pattern!(AStruct {
+        contains_each![matches_pattern!(&AStruct {
             get_value(): eq(123)
         })]
     )
@@ -48,24 +48,139 @@
 fn contains_each_works_as_inner_matcher() -> Result<()> {
     #[derive(Debug)]
     struct AStruct(Vec<i32>);
-    verify_that!(AStruct(vec![123]), matches_pattern!(AStruct(contains_each![eq(123)])))
+    verify_that!(AStruct(vec![123]), matches_pattern!(&AStruct(ref contains_each![eq(&123)])))
 }
 
 #[test]
 fn pointwise_works_as_inner_matcher() -> Result<()> {
     #[derive(Debug)]
     struct AStruct(Vec<i32>);
-    verify_that!(AStruct(vec![123]), matches_pattern!(AStruct(pointwise!(eq, [123]))))
+    verify_that!(AStruct(vec![123]), matches_pattern!(&AStruct(ref pointwise!(eq, [&123]))))
 }
 
 #[test]
 fn elements_are_works_as_inner_matcher() -> Result<()> {
     #[derive(Debug)]
     struct AStruct(Vec<i32>);
-    verify_that!(AStruct(vec![123]), matches_pattern!(AStruct(elements_are![eq(123)])))
+    verify_that!(AStruct(vec![123]), matches_pattern!(&AStruct(ref elements_are![eq(&123)])))
 }
 
 #[test]
 fn tuple_works_as_inner_matcher() -> Result<()> {
-    verify_that!(vec![(123,)], elements_are![(eq(123),)])
+    verify_that!(vec![(123,)], elements_are![(eq(&123),)])
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_of_non_copy_value() -> Result<()> {
+    #[derive(Debug)]
+    struct AnInnerStruct;
+
+    #[derive(Debug)]
+    struct AStruct;
+
+    impl AStruct {
+        fn get_value(&self) -> Option<AnInnerStruct> {
+            Some(AnInnerStruct)
+        }
+    }
+
+    verify_that!(
+        AStruct,
+        matches_pattern!(&AStruct {
+            get_value(): ref some(matches_pattern!(&AnInnerStruct))
+        })
+    )
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_of_non_copy_enum() -> Result<()> {
+    #[derive(Debug)]
+    enum AnInnerStruct {
+        ThisCase,
+        #[allow(unused)]
+        ThatCase,
+    }
+    #[derive(Debug)]
+    struct AStruct;
+    impl AStruct {
+        fn get_value(&self) -> Option<AnInnerStruct> {
+            Some(AnInnerStruct::ThisCase)
+        }
+    }
+
+    verify_that!(
+        AStruct,
+        matches_pattern!(&AStruct {
+            get_value(): ref some(matches_pattern!(&AnInnerStruct::ThisCase))
+        })
+    )
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_ref_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    struct AnInnerStruct;
+    #[derive(Debug)]
+    struct AStruct;
+    impl AStruct {
+        fn get_value(&self) -> Option<AnInnerStruct> {
+            Some(AnInnerStruct)
+        }
+    }
+
+    verify_that!(
+        AStruct,
+        matches_pattern!(AStruct {
+            get_value(): some(matches_pattern!(AnInnerStruct))
+        })
+    )
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_enum_ref_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    enum AnInnerStruct {
+        ThisCase,
+        #[allow(unused)]
+        ThatCase,
+    }
+    #[derive(Debug)]
+    struct AStruct;
+    impl AStruct {
+        fn get_value(&self) -> Option<AnInnerStruct> {
+            Some(AnInnerStruct::ThisCase)
+        }
+    }
+
+    verify_that!(
+        AStruct,
+        matches_pattern!(AStruct {
+            get_value(): some(matches_pattern!(AnInnerStruct::ThisCase))
+        })
+    )
+}
+
+#[test]
+fn matches_struct_with_property_against_predicate() -> Result<()> {
+    #[derive(Debug)]
+    enum AnInnerStruct {
+        ThisCase,
+        #[allow(unused)]
+        ThatCase,
+    }
+
+    #[derive(Debug)]
+    struct AStruct;
+    impl AStruct {
+        fn get_value(&self) -> AnInnerStruct {
+            AnInnerStruct::ThisCase
+        }
+    }
+
+    verify_that!(
+        AStruct,
+        matches_pattern!(AStruct {
+            get_value(): predicate(|_: &_| true)
+        })
+    )
 }
diff --git a/crates/googletest/tests/elements_are_matcher_test.rs b/crates/googletest/tests/elements_are_matcher_test.rs
index 4de2314..3aafbc7 100644
--- a/crates/googletest/tests/elements_are_matcher_test.rs
+++ b/crates/googletest/tests/elements_are_matcher_test.rs
@@ -12,21 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::Matcher;
 use googletest::prelude::*;
 use indoc::indoc;
 
 #[test]
 fn elements_are_matches_vector() -> Result<()> {
     let value = vec![1, 2, 3];
-    verify_that!(value, elements_are![eq(1), eq(2), eq(3)])
+    verify_that!(value, elements_are![eq(&1), eq(&2), eq(&3)])
 }
 
 #[test]
 fn elements_are_matches_slice() -> Result<()> {
     let value = vec![1, 2, 3];
     let slice = value.as_slice();
-    verify_that!(*slice, elements_are![eq(1), eq(2), eq(3)])
+    verify_that!(slice, elements_are![eq(&1), eq(&2), eq(&3)])
 }
 
 #[test]
@@ -37,13 +36,13 @@
 #[test]
 fn elements_are_supports_trailing_comma() -> Result<()> {
     let value = vec![1, 2, 3];
-    verify_that!(value, elements_are![eq(1), eq(2), eq(3),])
+    verify_that!(value, elements_are![eq(&1), eq(&2), eq(&3),])
 }
 
 #[test]
 fn elements_are_returns_no_match_when_expected_and_actual_sizes_differ() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, not(elements_are![eq(1), eq(2), eq(3)]))
+    verify_that!(value, not(elements_are![eq(&1), eq(&2), eq(&3)]))
 }
 
 #[test]
@@ -51,12 +50,32 @@
     #[derive(Debug, PartialEq)]
     struct AStruct(i32);
     let expected_value = AStruct(123);
-    verify_that!(vec![AStruct(123)], elements_are![eq_deref_of(&expected_value)])
+    verify_that!(vec![AStruct(123)], elements_are![eq(&expected_value)])
+}
+
+#[test]
+fn elements_are_matches_iterator_returning_by_value() -> Result<()> {
+    #[derive(Debug, Copy, Clone)]
+    struct Countdown(i32);
+    impl Iterator for Countdown {
+        type Item = i32;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            match self.0 {
+                0 => None,
+                x => {
+                    self.0 -= 1;
+                    Some(x)
+                }
+            }
+        }
+    }
+    verify_that!(Countdown(3), elements_are![eq(3), eq(2), eq(1)])
 }
 
 #[test]
 fn elements_are_produces_correct_failure_message() -> Result<()> {
-    let result = verify_that!(vec![1, 4, 3], elements_are![eq(1), eq(2), eq(3)]);
+    let result = verify_that!(vec![1, 4, 3], elements_are![eq(&1), eq(&2), eq(&3)]);
     verify_that!(
         result,
         err(displays_as(contains_substring(indoc!(
@@ -76,7 +95,7 @@
 fn elements_are_produces_correct_failure_message_nested() -> Result<()> {
     let result = verify_that!(
         vec![vec![0, 1], vec![1, 2]],
-        elements_are![elements_are![eq(1), eq(2)], elements_are![eq(2), eq(3)]]
+        elements_are![elements_are![eq(&1), eq(&2)], elements_are![eq(&2), eq(&3)]]
     );
     verify_that!(
         result,
@@ -103,14 +122,12 @@
 
 #[test]
 fn elements_are_explain_match_wrong_size() -> Result<()> {
-    verify_that!(
-        elements_are![eq(1)].explain_match(&vec![1, 2]),
-        displays_as(eq("whose size is 2"))
-    )
+    let matcher = elements_are![eq(&1)];
+    verify_that!(matcher.explain_match(&vec![1, 2]), displays_as(eq("whose size is 2")))
 }
 
-fn create_matcher() -> impl Matcher<ActualT = Vec<i32>> {
-    elements_are![eq(1)]
+fn create_matcher<'a>() -> impl Matcher<&'a Vec<i32>> {
+    elements_are![eq(&1)]
 }
 
 #[test]
@@ -120,5 +137,10 @@
 
 #[test]
 fn elements_are_implicitly_called() -> Result<()> {
-    verify_that!(vec![1, 2, 3], [eq(1), eq(2), eq(3)])
+    verify_that!(vec![1, 2, 3], [eq(&1), eq(&2), eq(&3)])
+}
+
+#[test]
+fn elements_are_with_auto_eq() -> Result<()> {
+    verify_that!(vec![1, 2, 3], [&1, &2, lt(&43)])
 }
diff --git a/crates/googletest/tests/field_matcher_test.rs b/crates/googletest/tests/field_matcher_test.rs
index f585c21..c239b47 100644
--- a/crates/googletest/tests/field_matcher_test.rs
+++ b/crates/googletest/tests/field_matcher_test.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::matcher::MatcherResult;
 use googletest::prelude::*;
 
 #[derive(Debug)]
@@ -22,7 +22,7 @@
 
 #[test]
 fn field_matches_integer_field() -> Result<()> {
-    verify_that!(IntField { int: 32 }, field!(IntField.int, eq(32)))
+    verify_that!(IntField { int: 32 }, field!(&IntField.int, eq(32)))
 }
 
 #[derive(Debug)]
@@ -32,12 +32,15 @@
 
 #[test]
 fn field_matches_string_field() -> Result<()> {
-    verify_that!(StringField { strink: "yes".to_string() }, field!(StringField.strink, eq("yes")))
+    verify_that!(
+        StringField { strink: "yes".to_string() },
+        field!(&StringField.strink, ref eq("yes"))
+    )
 }
 
 #[test]
 fn field_error_message_shows_field_name_and_inner_matcher() -> Result<()> {
-    let matcher = field!(IntField.int, eq(31));
+    let matcher = field!(&IntField.int, eq(31));
 
     verify_that!(
         matcher.describe(MatcherResult::Match),
@@ -54,15 +57,15 @@
 
 #[test]
 fn struct_in_other_module_matches() -> Result<()> {
-    verify_that!(sub::SubStruct { field: 32 }, field!(sub::SubStruct.field, eq(32)))
+    verify_that!(sub::SubStruct { field: 32 }, field!(&sub::SubStruct.field, eq(32)))
 }
 
 #[derive(Debug)]
-struct Tuple(i32, String);
+struct Tuple(i32, #[allow(unused)] String);
 
 #[test]
 fn tuple_matches_with_index() -> Result<()> {
-    verify_that!(Tuple(32, "yes".to_string()), field!(Tuple.0, eq(32)))
+    verify_that!(Tuple(32, "yes".to_string()), field!(&Tuple.0, eq(32)))
 }
 
 #[test]
@@ -73,7 +76,7 @@
     }
     let value = AnEnum::AValue(123);
 
-    verify_that!(value, field!(AnEnum::AValue.0, eq(123)))
+    verify_that!(value, field!(&AnEnum::AValue.0, eq(123)))
 }
 
 #[test]
@@ -84,7 +87,7 @@
     }
     let value = AStruct { a: vec![1] };
 
-    let result = verify_that!(value, field!(AStruct.a, container_eq([])));
+    let result = verify_that!(value, field!(&AStruct.a, ref container_eq([])));
 
     verify_that!(
         result,
@@ -104,7 +107,7 @@
     }
     let value = AnEnum::AnotherValue;
 
-    verify_that!(value, not(field!(AnEnum::AValue.0, eq(123))))
+    verify_that!(&value, not(field!(&AnEnum::AValue.0, eq(123))))
 }
 
 #[test]
@@ -119,7 +122,7 @@
     }
     let value = AnEnum::AnotherValue;
 
-    let result = verify_that!(value, field!(AnEnum::AValue.a, eq(123)));
+    let result = verify_that!(value, field!(&AnEnum::AValue.a, eq(123)));
 
     verify_that!(
         result,
@@ -133,11 +136,12 @@
     enum AnEnum {
         #[allow(dead_code)] // This variant is intentionally unused.
         AValue(u32),
+        #[allow(unused)]
         AnotherValue(u32),
     }
     let value = AnEnum::AnotherValue(123);
 
-    let result = verify_that!(value, field!(AnEnum::AValue.0, eq(123)));
+    let result = verify_that!(value, field!(&AnEnum::AValue.0, eq(123)));
 
     verify_that!(
         result,
@@ -158,7 +162,7 @@
     }
     let value = AnEnum::AnotherValue { a: 123 };
 
-    let result = verify_that!(value, field!(AnEnum::AValue.0, eq(123)));
+    let result = verify_that!(value, field!(&AnEnum::AValue.0, eq(123)));
 
     verify_that!(
         result,
@@ -174,5 +178,87 @@
     }
     let value = AnEnum::AValue { a_field: 123 };
 
-    verify_that!(value, field!(AnEnum::AValue.a_field, eq(123)))
+    verify_that!(value, field!(&AnEnum::AValue.a_field, eq(123)))
+}
+
+#[test]
+fn matches_struct_copy_to_copy() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct Strukt {
+        a_field: i32,
+    }
+
+    verify_that!(Strukt { a_field: 32 }, field!(Strukt.a_field, eq(32)))
+}
+
+#[test]
+fn matches_struct_ref_to_copy() -> Result<()> {
+    #[derive(Debug)]
+    struct Strukt {
+        a_field: i32,
+    }
+
+    verify_that!(Strukt { a_field: 32 }, field!(&Strukt.a_field, eq(32)))
+}
+
+#[test]
+fn matches_struct_ref_to_ref() -> Result<()> {
+    #[derive(Debug)]
+    struct Strukt {
+        a_field: String,
+    }
+
+    verify_that!(Strukt { a_field: "32".into() }, field!(&Strukt.a_field, ref eq("32")))
+}
+
+#[test]
+fn matches_struct_copy_to_ref() -> Result<()> {
+    // It is not possible to have a copy struct with non-copy field. Hence, this
+    // test case is not necessary.
+    Ok(())
+}
+
+#[test]
+fn matches_struct_ref_to_ref_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    struct Strukt {
+        a_field: String,
+    }
+
+    verify_that!(Strukt { a_field: "32".into() }, field!(Strukt.a_field, eq("32")))
+}
+
+#[test]
+fn matches_struct_with_auto_eq() -> Result<()> {
+    #[derive(Debug)]
+    struct Strukt {
+        a_field: String,
+    }
+
+    verify_that!(Strukt { a_field: "32".into() }, field!(Strukt.a_field, "32"))
+}
+
+#[test]
+fn matches_enum_with_auto_eq() -> Result<()> {
+    #[derive(Debug)]
+    enum Enum {
+        Str(String),
+        #[allow(unused)]
+        Int(i32),
+    }
+
+    verify_that!(Enum::Str("32".into()), field!(Enum::Str.0, "32"))
+}
+
+#[test]
+fn matches_enum_with_auto_eq_with_wrapper() -> Result<()> {
+    #[derive(Debug)]
+    struct Wrapper<I> {
+        wrapped: I,
+    }
+
+    verify_that!(
+        Wrapper { wrapped: Wrapper { wrapped: 23 } },
+        field!(Wrapper.wrapped, field!(Wrapper.wrapped, &23))
+    )
 }
diff --git a/crates/googletest/tests/fmt_test.rs b/crates/googletest/tests/fmt_test.rs
new file mode 100644
index 0000000..63e2dd7
--- /dev/null
+++ b/crates/googletest/tests/fmt_test.rs
@@ -0,0 +1,58 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+mod write_expr_value {
+    use googletest::prelude::*;
+
+    // Converts the formatting call to a `String` for testing.
+    macro_rules! write_expr_value {
+        ($expr_str:expr, $expr: expr $(,)?) => {{
+            let mut s = String::new();
+            ::googletest::fmt::internal::__googletest__write_expr_value!(s, $expr_str, $expr);
+            s
+        }};
+    }
+
+    #[test]
+    fn test_with_debug_value_references() -> Result<()> {
+        #[derive(Debug)]
+        struct Foo;
+        let mut val = Foo;
+
+        verify_that!(write_expr_value!("val", val), eq("\n  val = Foo,"))?;
+        verify_that!(write_expr_value!("val", &val), eq("\n  val = Foo,"))?;
+        verify_that!(write_expr_value!("val", &&val), eq("\n  val = Foo,"))?;
+        verify_that!(write_expr_value!("val", &mut val), eq("\n  val = Foo,"))?;
+        verify_that!(write_expr_value!("val", &mut &mut val), eq("\n  val = Foo,"))?;
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_with_non_debug_value_references() -> Result<()> {
+        struct Foo;
+        let mut val = Foo;
+
+        verify_that!(write_expr_value!("val", val), eq("\n  val does not implement Debug,"))?;
+        verify_that!(write_expr_value!("val", &val), eq("\n  val does not implement Debug,"))?;
+        verify_that!(write_expr_value!("val", &&val), eq("\n  val does not implement Debug,"))?;
+        verify_that!(write_expr_value!("val", &mut val), eq("\n  val does not implement Debug,"))?;
+        verify_that!(
+            write_expr_value!("val", &mut &mut val),
+            eq("\n  val does not implement Debug,")
+        )?;
+
+        Ok(())
+    }
+}
diff --git a/crates/googletest/tests/lib.rs b/crates/googletest/tests/lib.rs
index fb243ca..f5d8f75 100644
--- a/crates/googletest/tests/lib.rs
+++ b/crates/googletest/tests/lib.rs
@@ -14,6 +14,7 @@
 
 mod all_matcher_test;
 mod any_matcher_test;
+mod assertions_test;
 mod colorized_diff_test;
 mod composition_test;
 mod elements_are_matcher_test;
@@ -21,7 +22,6 @@
 mod matches_pattern_test;
 mod pointwise_matcher_test;
 mod property_matcher_test;
-#[cfg(feature = "proptest")]
 mod proptest_integration_test;
 mod tuple_matcher_test;
 mod unordered_elements_are_matcher_test;
diff --git a/crates/googletest/tests/matches_pattern_test.rs b/crates/googletest/tests/matches_pattern_test.rs
index 3298e36..d1b97e7 100644
--- a/crates/googletest/tests/matches_pattern_test.rs
+++ b/crates/googletest/tests/matches_pattern_test.rs
@@ -23,7 +23,7 @@
     }
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { a_field: eq(123) }))
+    verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(123) }))
 }
 
 #[test]
@@ -35,7 +35,7 @@
     }
     let actual = AStruct { a_field: 123, another_field: 234 };
 
-    verify_that!(actual, matches_pattern!(AStruct { a_field: eq(123), another_field: eq(234) }))
+    verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(123), another_field: eq(234) }))
 }
 
 #[test]
@@ -47,7 +47,7 @@
     }
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct {
+    verify_that!(actual, matches_pattern!(&AStruct {
         a_field: eq(123), // Block reformatting
     }))
 }
@@ -63,7 +63,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             a_field: eq(123),
             another_field: eq(234), // Block reformatting
         })
@@ -82,7 +82,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             a_field: eq(123),
             another_field: eq(234),
             a_third_field: eq(345),
@@ -104,29 +104,69 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { a_nested_struct: pat!(ANestedStruct { a_field: eq(123) }) })
+        matches_pattern!(&AStruct { a_nested_struct: ref pat!(&ANestedStruct { a_field: eq(123) }) })
     )
 }
 
 #[test]
+fn matches_struct_containing_nested_struct_with_field_with_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct {
+        a_nested_struct: ANestedStruct,
+    }
+    #[derive(Debug)]
+    struct ANestedStruct {
+        a_field: u32,
+    }
+    let actual = AStruct { a_nested_struct: ANestedStruct { a_field: 123 } };
+
+    verify_that!(
+        actual,
+        matches_pattern!(AStruct { a_nested_struct: pat!(ANestedStruct { a_field: eq(&123) }) })
+    )
+}
+
+#[test]
+fn matches_struct_containing_non_copy_field_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct {
+        a_string: String,
+    }
+    let actual = AStruct { a_string: "123".into() };
+
+    verify_that!(actual, matches_pattern!(AStruct { a_string: eq("123") }))
+}
+
+#[test]
 fn has_correct_assertion_failure_message_for_single_field() -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
     }
     let actual = AStruct { a_field: 123 };
-    let result = verify_that!(actual, matches_pattern!(AStruct { a_field: eq(234) }));
+    let result = verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(234) }));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(indoc! {"
-            Value of: actual
-            Expected: is AStruct which has field `a_field`, which is equal to 234
-            Actual: AStruct { a_field: 123 },
-              which has field `a_field`, which isn't equal to 234
-            "
-        })))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = indoc!(
+        "
+        Value of: actual
+        Expected: is & AStruct which has field `a_field`, which is equal to 234
+        Actual: AStruct { a_field: 123 },
+          which has field `a_field`, which isn't equal to 234
+        "
+    );
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = indoc!(
+        "
+        Value of: actual
+        Expected: is &AStruct which has field `a_field`, which is equal to 234
+        Actual: AStruct { a_field: 123 },
+          which has field `a_field`, which isn't equal to 234
+        "
+    );
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
 
 #[test]
@@ -139,21 +179,34 @@
     let actual = AStruct { a_field: 123, another_field: 234 };
     let result = verify_that!(
         actual,
-        matches_pattern!(AStruct { a_field: eq(234), another_field: eq(123) })
+        matches_pattern!(&AStruct { a_field: eq(234), another_field: eq(123) })
     );
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(indoc!(
-            "
-            Value of: actual
-            Expected: is AStruct which has all the following properties:
-              * has field `a_field`, which is equal to 234
-              * has field `another_field`, which is equal to 123
-            Actual: AStruct { a_field: 123, another_field: 234 },
-              * which has field `a_field`, which isn't equal to 234
-              * which has field `another_field`, which isn't equal to 123"
-        ))))
-    )
+
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = indoc!(
+        "
+        Value of: actual
+        Expected: is & AStruct which has all the following properties:
+          * has field `a_field`, which is equal to 234
+          * has field `another_field`, which is equal to 123
+        Actual: AStruct { a_field: 123, another_field: 234 },
+          * which has field `a_field`, which isn't equal to 234
+          * which has field `another_field`, which isn't equal to 123"
+    );
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = indoc!(
+        "
+        Value of: actual
+        Expected: is &AStruct which has all the following properties:
+          * has field `a_field`, which is equal to 234
+          * has field `another_field`, which is equal to 123
+        Actual: AStruct { a_field: 123, another_field: 234 },
+          * which has field `a_field`, which isn't equal to 234
+          * which has field `another_field`, which isn't equal to 123"
+    );
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
 
 #[test]
@@ -171,33 +224,47 @@
     let actual = AStruct { a_field: 123, another_field: 234 };
     let result = verify_that!(
         actual,
-        matches_pattern!(AStruct { get_field(): eq(234), another_field: eq(123) })
+        matches_pattern!(&AStruct { get_field(): eq(234), another_field: eq(123) })
     );
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(indoc!(
-            "
-            Value of: actual
-            Expected: is AStruct which has all the following properties:
-              * has property `get_field ()`, which is equal to 234
-              * has field `another_field`, which is equal to 123
-            Actual: AStruct { a_field: 123, another_field: 234 },
-              * whose property `get_field ()` is `123`, which isn't equal to 234
-              * which has field `another_field`, which isn't equal to 123"
-        ))))
-    )
+
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = indoc!(
+        "
+    Value of: actual
+    Expected: is & AStruct which has all the following properties:
+      * has property `get_field ()`, which is equal to 234
+      * has field `another_field`, which is equal to 123
+    Actual: AStruct { a_field: 123, another_field: 234 },
+      * whose property `get_field ()` is `123`, which isn't equal to 234
+      * which has field `another_field`, which isn't equal to 123"
+    );
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = indoc!(
+        "
+    Value of: actual
+    Expected: is &AStruct which has all the following properties:
+      * has property `get_field ()`, which is equal to 234
+      * has field `another_field`, which is equal to 123
+    Actual: AStruct { a_field: 123, another_field: 234 },
+      * whose property `get_field ()` is `123`, which isn't equal to 234
+      * which has field `another_field`, which isn't equal to 123"
+    );
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
 
 #[test]
 fn has_meaningful_assertion_failure_message_when_wrong_enum_variant_is_used() -> Result<()> {
     #[derive(Debug)]
     enum AnEnum {
+        #[allow(unused)]
         A(u32),
         #[allow(unused)]
         B(u32),
     }
     let actual = AnEnum::A(123);
-    let result = verify_that!(actual, matches_pattern!(AnEnum::B(eq(123))));
+    let result = verify_that!(actual, matches_pattern!(&AnEnum::B(eq(123))));
 
     verify_that!(
         result,
@@ -219,7 +286,7 @@
     }
     let actual = a_module::AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(a_module::AStruct { a_field: eq(123) }))
+    verify_that!(actual, matches_pattern!(&a_module::AStruct { a_field: eq(123) }))
 }
 
 #[test]
@@ -228,7 +295,7 @@
     struct AStruct(u32);
     let actual = AStruct(123);
 
-    verify_that!(actual, matches_pattern!(AStruct(eq(123))))
+    verify_that!(actual, matches_pattern!(&AStruct(eq(123))))
 }
 
 #[test]
@@ -237,7 +304,7 @@
     struct AStruct(u32, u32);
     let actual = AStruct(123, 234);
 
-    verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234))))
+    verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234))))
 }
 
 #[test]
@@ -246,7 +313,7 @@
     struct AStruct(u32, u32, u32);
     let actual = AStruct(123, 234, 345);
 
-    verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234), eq(345))))
+    verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234), eq(345))))
 }
 
 #[test]
@@ -255,7 +322,7 @@
     struct AStruct(u32, u32, u32, u32);
     let actual = AStruct(123, 234, 345, 456);
 
-    verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456))))
+    verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456))))
 }
 
 #[test]
@@ -264,7 +331,7 @@
     struct AStruct(u32, u32, u32, u32, u32);
     let actual = AStruct(123, 234, 345, 456, 567);
 
-    verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456), eq(567))))
+    verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456), eq(567))))
 }
 
 #[test]
@@ -275,7 +342,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678)))
+        matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678)))
     )
 }
 
@@ -287,7 +354,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678), eq(789)))
+        matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678), eq(789)))
     )
 }
 
@@ -299,7 +366,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(
+        matches_pattern!(&AStruct(
             eq(123),
             eq(234),
             eq(345),
@@ -320,7 +387,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(
+        matches_pattern!(&AStruct(
             eq(123),
             eq(234),
             eq(345),
@@ -342,7 +409,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(
+        matches_pattern!(&AStruct(
             eq(123),
             eq(234),
             eq(345),
@@ -358,6 +425,29 @@
 }
 
 #[test]
+fn matches_tuple_struct_containing_ten_fields_by_ref() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct(u32, u32, u32, u32, u32, u32, u32, u32, u32, u32);
+    let actual = AStruct(123, 234, 345, 456, 567, 678, 789, 890, 901, 12);
+
+    verify_that!(
+        actual,
+        matches_pattern!(&AStruct(
+            ref eq(&123),
+            ref eq(&234),
+            ref eq(&345),
+            ref eq(&456),
+            ref eq(&567),
+            ref eq(&678),
+            ref eq(&789),
+            ref eq(&890),
+            ref eq(&901),
+            ref eq(&12)
+        ))
+    )
+}
+
+#[test]
 fn matches_tuple_struct_with_trailing_comma() -> Result<()> {
     #[derive(Debug)]
     struct AStruct(u32);
@@ -365,7 +455,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(
+        matches_pattern!(&AStruct(
             eq(123), // Keep the trailing comma, block reformatting
         ))
     )
@@ -379,7 +469,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct(
+        matches_pattern!(&AStruct(
             eq(123),
             eq(234), // Keep the trailing comma, block reformatting
         ))
@@ -394,14 +484,30 @@
     }
     let actual = AnEnum::A;
 
+    verify_that!(actual, matches_pattern!(&AnEnum::A))
+}
+
+#[test]
+fn matches_enum_without_field_ref_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    enum AnEnum {
+        A,
+    }
+    let actual = AnEnum::A;
+
     verify_that!(actual, matches_pattern!(AnEnum::A))
 }
 
-#[rustversion::before(1.76)]
-const ANENUM_A_REPR: &str = "AnEnum :: A";
+#[test]
+fn matches_enum_without_field_copy() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    enum AnEnum {
+        A,
+    }
+    let actual = AnEnum::A;
 
-#[rustversion::since(1.76)]
-const ANENUM_A_REPR: &str = "AnEnum::A";
+    verify_that!(actual, matches_pattern!(AnEnum::A))
+}
 
 #[test]
 fn generates_correct_failure_output_when_enum_variant_without_field_is_not_matched() -> Result<()> {
@@ -413,11 +519,16 @@
     }
     let actual = AnEnum::B;
 
-    let result = verify_that!(actual, matches_pattern!(AnEnum::A));
+    let result = verify_that!(actual, matches_pattern!(&AnEnum::A));
 
-    verify_that!(result, err(displays_as(contains_substring(format!("is not {ANENUM_A_REPR}")))))
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "is not & AnEnum :: A";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "is not &AnEnum::A";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn generates_correct_failure_output_when_enum_variant_without_field_is_matched() -> Result<()> {
     #[derive(Debug)]
@@ -426,11 +537,16 @@
     }
     let actual = AnEnum::A;
 
-    let result = verify_that!(actual, not(matches_pattern!(AnEnum::A)));
+    let result = verify_that!(actual, not(matches_pattern!(&AnEnum::A)));
 
-    verify_that!(result, err(displays_as(contains_substring(format!("is {ANENUM_A_REPR}")))))
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "is & AnEnum :: A";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "is &AnEnum::A";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn matches_enum_with_field() -> Result<()> {
     #[derive(Debug)]
@@ -439,9 +555,8 @@
     }
     let actual = AnEnum::A(123);
 
-    verify_that!(actual, matches_pattern!(AnEnum::A(eq(123))))
+    verify_that!(actual, matches_pattern!(&AnEnum::A(eq(123))))
 }
-
 #[test]
 fn does_not_match_wrong_enum_value() -> Result<()> {
     #[derive(Debug)]
@@ -452,9 +567,8 @@
     }
     let actual = AnEnum::B;
 
-    verify_that!(actual, not(matches_pattern!(AnEnum::A(eq(123)))))
+    verify_that!(actual, not(matches_pattern!(&AnEnum::A(eq(123)))))
 }
-
 #[test]
 fn includes_enum_variant_in_description_with_field() -> Result<()> {
     #[derive(Debug)]
@@ -463,16 +577,16 @@
     }
     let actual = AnEnum::A(123);
 
-    let result = verify_that!(actual, matches_pattern!(AnEnum::A(eq(234))));
+    let result = verify_that!(actual, matches_pattern!(&AnEnum::A(eq(234))));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(format!(
-            "Expected: is {ANENUM_A_REPR} which has field `0`"
-        ))))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AnEnum :: A which has field `0`";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AnEnum::A which has field `0`";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn includes_enum_variant_in_negative_description_with_field() -> Result<()> {
     #[derive(Debug)]
@@ -481,16 +595,16 @@
     }
     let actual = AnEnum::A(123);
 
-    let result = verify_that!(actual, not(matches_pattern!(AnEnum::A(eq(123)))));
+    let result = verify_that!(actual, not(matches_pattern!(&AnEnum::A(eq(123)))));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(format!(
-            "Expected: is not {ANENUM_A_REPR} which has field `0`, which is equal to"
-        ))))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is not & AnEnum :: A which has field `0`, which is equal to";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is not &AnEnum::A which has field `0`, which is equal to";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn includes_enum_variant_in_description_with_two_fields() -> Result<()> {
     #[derive(Debug)]
@@ -499,16 +613,16 @@
     }
     let actual = AnEnum::A(123, 234);
 
-    let result = verify_that!(actual, matches_pattern!(AnEnum::A(eq(234), eq(234))));
+    let result = verify_that!(actual, matches_pattern!(&AnEnum::A(eq(234), eq(234))));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(format!(
-            "Expected: is {ANENUM_A_REPR} which has all the following properties"
-        ))))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AnEnum :: A which has all the following properties";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AnEnum::A which has all the following properties";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn includes_enum_variant_in_description_with_three_fields() -> Result<()> {
     #[derive(Debug)]
@@ -517,16 +631,16 @@
     }
     let actual = AnEnum::A(123, 234, 345);
 
-    let result = verify_that!(actual, matches_pattern!(AnEnum::A(eq(234), eq(234), eq(345))));
+    let result = verify_that!(actual, matches_pattern!(&AnEnum::A(eq(234), eq(234), eq(345))));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(format!(
-            "Expected: is {ANENUM_A_REPR} which has all the following properties"
-        ))))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AnEnum :: A which has all the following properties";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AnEnum::A which has all the following properties";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn includes_enum_variant_in_description_with_named_field() -> Result<()> {
     #[derive(Debug)]
@@ -535,16 +649,16 @@
     }
     let actual = AnEnum::A { field: 123 };
 
-    let result = verify_that!(actual, matches_pattern!(AnEnum::A { field: eq(234) }));
+    let result = verify_that!(actual, matches_pattern!(&AnEnum::A { field: eq(234) }));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(format!(
-            "Expected: is {ANENUM_A_REPR} which has field `field`"
-        ))))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AnEnum :: A which has field `field`";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AnEnum::A which has field `field`";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn includes_enum_variant_in_description_with_two_named_fields() -> Result<()> {
     #[derive(Debug)]
@@ -555,17 +669,17 @@
 
     let result = verify_that!(
         actual,
-        matches_pattern!(AnEnum::A { field: eq(234), another_field: eq(234) })
+        matches_pattern!(&AnEnum::A { field: eq(234), another_field: eq(234) })
     );
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(format!(
-            "Expected: is {ANENUM_A_REPR} which has all the following properties"
-        ))))
-    )
-}
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AnEnum :: A which has all the following properties";
 
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AnEnum::A which has all the following properties";
+
+    verify_that!(&result, err(displays_as(contains_substring(EXPECTED))))
+}
 #[test]
 fn includes_struct_name_in_description_with_property() -> Result<()> {
     #[derive(Debug)]
@@ -579,16 +693,16 @@
     }
     let actual = AStruct { field: 123 };
 
-    let result = verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(234) }));
+    let result = verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(234) }));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(
-            "Expected: is AStruct which has property `get_field ()`"
-        )))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AStruct which has property `get_field ()`";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AStruct which has property `get_field ()`";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn includes_struct_name_in_description_with_ref_property() -> Result<()> {
     #[derive(Debug)]
@@ -602,14 +716,15 @@
     }
     let actual = AStruct { field: 123 };
 
-    let result = verify_that!(actual, matches_pattern!(AStruct { *get_field(): eq(234) }));
+    let result = verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(&234) }));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(
-            "Expected: is AStruct which has property `get_field ()`"
-        )))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AStruct which has property `get_field ()`";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AStruct which has property `get_field ()`";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
 
 #[test]
@@ -626,14 +741,15 @@
     let actual = AStruct { field: 123 };
 
     let result =
-        verify_that!(actual, matches_pattern!(AStruct { field: eq(123), get_field(): eq(234) }));
+        verify_that!(actual, matches_pattern!(&AStruct { field: eq(123), get_field(): eq(234) }));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(
-            "Expected: is AStruct which has all the following properties"
-        )))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AStruct which has all the following properties";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AStruct which has all the following properties";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
 
 #[test]
@@ -650,16 +766,16 @@
     let actual = AStruct { field: 123 };
 
     let result =
-        verify_that!(actual, matches_pattern!(AStruct { field: eq(123), *get_field(): eq(234) }));
+        verify_that!(actual, matches_pattern!(&AStruct { field: eq(123), get_field(): eq(&234) }));
 
-    verify_that!(
-        result,
-        err(displays_as(contains_substring(
-            "Expected: is AStruct which has all the following properties"
-        )))
-    )
+    #[rustversion::before(1.76)]
+    const EXPECTED: &str = "Expected: is & AStruct which has all the following properties";
+
+    #[rustversion::since(1.76)]
+    const EXPECTED: &str = "Expected: is &AStruct which has all the following properties";
+
+    verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
 }
-
 #[test]
 fn matches_struct_with_a_method() -> Result<()> {
     #[derive(Debug)]
@@ -675,9 +791,8 @@
 
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(123) }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(123) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_and_trailing_comma() -> Result<()> {
     #[derive(Debug)]
@@ -693,9 +808,8 @@
 
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(123), }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(123), }))
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_parameter() -> Result<()> {
     #[derive(Debug)]
@@ -711,9 +825,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { add_to_field(2): eq(3) }))
+    verify_that!(actual, matches_pattern!(&AStruct { add_to_field(2): eq(3) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters() -> Result<()> {
     #[derive(Debug)]
@@ -729,9 +842,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { add_product_to_field(2, 3): eq(7) }))
+    verify_that!(actual, matches_pattern!(&AStruct { add_product_to_field(2, 3): eq(7) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_enum_value_parameter() -> Result<()> {
     enum AnEnum {
@@ -751,9 +863,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { get_a_field(AnEnum::AVariant): eq(1) }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_a_field(AnEnum::AVariant): eq(1) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma() -> Result<()> {
     #[derive(Debug)]
@@ -769,9 +880,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { add_product_to_field(2, 3,): eq(7) }))
+    verify_that!(actual, matches_pattern!(&AStruct { add_product_to_field(2, 3,): eq(7) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_returning_a_reference() -> Result<()> {
     #[derive(Debug)]
@@ -787,9 +897,8 @@
 
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(): eq(123) }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(): eq(&123) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_returning_a_reference_with_trailing_comma() -> Result<()> {
     #[derive(Debug)]
@@ -805,9 +914,8 @@
 
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(): eq(123), }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(): eq(&123), }))
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters_ret_ref() -> Result<()> {
     #[derive(Debug)]
@@ -823,9 +931,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(2, 3): eq(1) }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(2, 3): eq(&1) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_returning_reference_taking_enum_value_parameter() -> Result<()> {
     enum AnEnum {
@@ -845,9 +952,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(AnEnum::AVariant): eq(1) }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(AnEnum::AVariant): eq(&1) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_ret_ref() -> Result<()> {
     #[derive(Debug)]
@@ -863,9 +969,8 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(2, 3,): eq(1) }))
+    verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(2, 3,): eq(&1) }))
 }
-
 #[test]
 fn matches_struct_with_a_method_followed_by_a_field() -> Result<()> {
     #[derive(Debug)]
@@ -882,9 +987,11 @@
 
     let actual = AStruct { a_field: 123, another_field: 234 };
 
-    verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(123), another_field: eq(234) }))
+    verify_that!(
+        actual,
+        matches_pattern!(&AStruct { get_field(): eq(123), another_field: eq(234) })
+    )
 }
-
 #[test]
 fn matches_struct_with_a_method_followed_by_a_field_with_trailing_comma() -> Result<()> {
     #[derive(Debug)]
@@ -903,10 +1010,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { get_field(): eq(123), another_field: eq(234), })
+        matches_pattern!(&AStruct { get_field(): eq(123), another_field: eq(234), })
     )
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters_and_field() -> Result<()> {
     #[derive(Debug)]
@@ -925,10 +1031,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { add_product_to_field(2, 3): eq(7), another_field: eq(123) })
+        matches_pattern!(&AStruct { add_product_to_field(2, 3): eq(7), another_field: eq(123) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_enum_value_parameter_followed_by_field() -> Result<()> {
     enum AnEnum {
@@ -951,10 +1056,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { get_field(AnEnum::AVariant): eq(1), another_field: eq(2) })
+        matches_pattern!(&AStruct { get_field(AnEnum::AVariant): eq(1), another_field: eq(2) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_and_field() -> Result<()>
 {
@@ -974,10 +1078,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { add_product_to_field(2, 3,): eq(7), another_field: eq(123) })
+        matches_pattern!(&AStruct { add_product_to_field(2, 3,): eq(7), another_field: eq(123) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_method_returning_reference_followed_by_a_field() -> Result<()> {
     #[derive(Debug)]
@@ -996,13 +1099,12 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { *get_field_ref(): eq(123), another_field: eq(234) })
+        matches_pattern!(&AStruct { get_field_ref(): eq(&123), another_field: eq(234) })
     )
 }
-
 #[test]
-fn matches_struct_with_a_method_returning_reference_followed_by_a_field_with_trailing_comma()
--> Result<()> {
+fn matches_struct_with_a_method_returning_reference_followed_by_a_field_with_trailing_comma(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1019,10 +1121,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { *get_field_ref(): eq(123), another_field: eq(234), })
+        matches_pattern!(&AStruct { get_field_ref(): eq(&123), another_field: eq(234), })
     )
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_two_parameters_ret_ref_and_field() -> Result<()> {
     #[derive(Debug)]
@@ -1041,10 +1142,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { *get_field_ref(2, 3): eq(1), another_field: eq(123) })
+        matches_pattern!(&AStruct { get_field_ref(2, 3): eq(&1), another_field: eq(123) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_method_taking_enum_value_param_ret_ref_followed_by_field() -> Result<()> {
     enum AnEnum {
@@ -1067,13 +1167,12 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { *get_field_ref(AnEnum::AVariant): eq(1), another_field: eq(2) })
+        matches_pattern!(&AStruct { get_field_ref(AnEnum::AVariant): eq(&1), another_field: eq(2) })
     )
 }
-
 #[test]
-fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_ret_ref_and_field()
--> Result<()> {
+fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_ret_ref_and_field(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1090,10 +1189,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { *get_field_ref(2, 3,): eq(1), another_field: eq(123) })
+        matches_pattern!(&AStruct { get_field_ref(2, 3,): eq(&1), another_field: eq(123) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method() -> Result<()> {
     #[derive(Debug)]
@@ -1110,9 +1208,11 @@
 
     let actual = AStruct { a_field: 123, another_field: 234 };
 
-    verify_that!(actual, matches_pattern!(AStruct { another_field: eq(234), get_field(): eq(123) }))
+    verify_that!(
+        actual,
+        matches_pattern!(&AStruct { another_field: eq(234), get_field(): eq(123) })
+    )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_with_trailing_comma() -> Result<()> {
     #[derive(Debug)]
@@ -1131,10 +1231,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), get_field(): eq(123), })
+        matches_pattern!(&AStruct { another_field: eq(234), get_field(): eq(123), })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_with_params() -> Result<()> {
     #[derive(Debug)]
@@ -1153,10 +1252,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), add_product_to_field(2, 3): eq(7) })
+        matches_pattern!(&AStruct { another_field: eq(234), add_product_to_field(2, 3): eq(7) })
     )
 }
-
 #[test]
 fn matches_struct_with_field_followed_by_method_taking_enum_value_param() -> Result<()> {
     enum AnEnum {
@@ -1179,10 +1277,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(2), get_field(AnEnum::AVariant): eq(1) })
+        matches_pattern!(&AStruct { another_field: eq(2), get_field(AnEnum::AVariant): eq(1) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma() -> Result<()> {
     #[derive(Debug)]
@@ -1201,10 +1298,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), add_product_to_field(2, 3,): eq(7) })
+        matches_pattern!(&AStruct { another_field: eq(234), add_product_to_field(2, 3,): eq(7) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_returning_reference() -> Result<()> {
     #[derive(Debug)]
@@ -1223,10 +1319,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(): eq(123) })
+        matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(): eq(&123) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_returning_ref_and_trailing_comma() -> Result<()>
 {
@@ -1246,10 +1341,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(): eq(123), })
+        matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(): eq(&123), })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_with_params_ret_ref() -> Result<()> {
     #[derive(Debug)]
@@ -1268,10 +1362,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(2, 3): eq(123) })
+        matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(2, 3): eq(&123) })
     )
 }
-
 #[test]
 fn matches_struct_with_field_followed_by_method_taking_enum_value_param_ret_ref() -> Result<()> {
     enum AnEnum {
@@ -1294,13 +1387,12 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(2), *get_field_ref(AnEnum::AVariant): eq(1) })
+        matches_pattern!(&AStruct { another_field: eq(2), get_field_ref(AnEnum::AVariant): eq(&1) })
     )
 }
-
 #[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_ret_ref()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_ret_ref(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1317,10 +1409,9 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(2, 3,): eq(123) })
+        matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(2, 3,): eq(&123) })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_followed_by_a_field() -> Result<()> {
     #[derive(Debug)]
@@ -1340,17 +1431,16 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
             get_field(): eq(123),
             a_third_field: eq(345)
         })
     )
 }
-
 #[test]
-fn matches_struct_with_a_field_followed_by_a_method_followed_by_a_field_with_trailing_comma()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_followed_by_a_field_with_trailing_comma(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1368,14 +1458,13 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
             get_field(): eq(123),
             a_third_field: eq(345),
         })
     )
 }
-
 #[test]
 fn matches_struct_with_a_field_followed_by_a_method_with_params_followed_by_a_field() -> Result<()>
 {
@@ -1396,7 +1485,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
             add_product_to_field(2, 3): eq(7),
             a_third_field: eq(345),
@@ -1405,8 +1494,8 @@
 }
 
 #[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_followed_by_a_field()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_followed_by_a_field(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1424,7 +1513,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
             add_product_to_field(2, 3,): eq(7),
             a_third_field: eq(345),
@@ -1433,8 +1522,8 @@
 }
 
 #[test]
-fn matches_struct_with_field_followed_by_method_taking_enum_value_param_followed_by_field()
--> Result<()> {
+fn matches_struct_with_field_followed_by_method_taking_enum_value_param_followed_by_field(
+) -> Result<()> {
     enum AnEnum {
         AVariant,
     }
@@ -1456,7 +1545,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(2),
             get_field(AnEnum::AVariant): eq(1),
             a_third_field: eq(3),
@@ -1483,17 +1572,17 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
-            *get_field_ref(): eq(123),
+            get_field_ref(): eq(&123),
             a_third_field: eq(345)
         })
     )
 }
 
 #[test]
-fn matches_struct_with_a_field_followed_by_a_method_ret_ref_followed_by_a_field_with_trailing_comma()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_ret_ref_followed_by_a_field_with_trailing_comma(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1511,17 +1600,17 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
-            *get_field_ref(): eq(123),
+            get_field_ref(): eq(&123),
             a_third_field: eq(345),
         })
     )
 }
 
 #[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_ret_ref_followed_by_a_field()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_ret_ref_followed_by_a_field(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1539,17 +1628,17 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
-            *get_field_ref(2, 3): eq(123),
+            get_field_ref(2, 3): eq(&123),
             a_third_field: eq(345),
         })
     )
 }
 
 #[test]
-fn matches_struct_with_field_followed_by_method_taking_enum_value_param_ret_ref_followed_by_field()
--> Result<()> {
+fn matches_struct_with_field_followed_by_method_taking_enum_value_param_ret_ref_followed_by_field(
+) -> Result<()> {
     enum AnEnum {
         AVariant,
     }
@@ -1571,17 +1660,17 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(2),
-            *get_field_ref(AnEnum::AVariant): eq(1),
+            get_field_ref(AnEnum::AVariant): eq(&1),
             a_third_field: eq(3),
         })
     )
 }
 
 #[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_trailing_comma_ret_ref_followed_by_a_field()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_trailing_comma_ret_ref_followed_by_a_field(
+) -> Result<()> {
     #[derive(Debug)]
     struct AStruct {
         a_field: u32,
@@ -1599,10 +1688,129 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct {
+        matches_pattern!(&AStruct {
             another_field: eq(234),
-            *get_field_ref(2, 3,): eq(123),
+            get_field_ref(2, 3,): eq(&123),
             a_third_field: eq(345),
         })
     )
 }
+
+#[test]
+fn matches_struct_field_copy() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct {
+        a_field: u32,
+    }
+
+    let actual = AStruct { a_field: 123 };
+
+    verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(123) }))
+}
+
+#[test]
+fn matches_struct_field_non_copy() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct {
+        a_field: String,
+    }
+
+    let actual = AStruct { a_field: "123".into() };
+
+    verify_that!(
+        actual,
+        matches_pattern!(&AStruct {
+            a_field: ref eq("123"),
+        })
+    )
+}
+
+#[test]
+fn matches_copy_struct_field_copy() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct AStruct {
+        a_field: i32,
+    }
+
+    let actual = AStruct { a_field: 123 };
+
+    verify_that!(actual, matches_pattern!(AStruct { a_field: eq(123) }))
+}
+
+#[test]
+fn matches_struct_property_copy() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct;
+
+    impl AStruct {
+        fn prop(&self) -> i32 {
+            123
+        }
+    }
+
+    let actual = AStruct;
+
+    verify_that!(actual, matches_pattern!(&AStruct { prop(): eq(123) }))
+}
+
+#[test]
+fn matches_struct_property_non_copy() -> Result<()> {
+    #[derive(Debug)]
+    struct AStruct;
+
+    impl AStruct {
+        fn prop(&self) -> String {
+            "123".into()
+        }
+    }
+
+    let actual = AStruct;
+
+    verify_that!(actual, matches_pattern!(&AStruct { prop(): ref eq("123") }))
+}
+
+#[test]
+fn matches_copy_struct_property_copy() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct AStruct;
+
+    impl AStruct {
+        fn prop(self) -> i32 {
+            123
+        }
+    }
+
+    let actual = AStruct;
+
+    verify_that!(actual, matches_pattern!(AStruct { prop(): eq(123) }))
+}
+
+#[test]
+fn matches_copy_struct_property_non_copy() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct AStruct;
+
+    impl AStruct {
+        fn prop(self) -> String {
+            "123".into()
+        }
+    }
+    let actual = AStruct;
+
+    verify_that!(actual, matches_pattern!(AStruct { prop(): ref eq("123") }))
+}
+
+#[test]
+fn matches_struct_auto_eq() -> Result<()> {
+    #[derive(Debug, Clone)]
+    struct AStruct {
+        int: i32,
+        string: String,
+        option: Option<i32>,
+    }
+
+    verify_that!(
+        AStruct { int: 123, string: "123".into(), option: Some(123) },
+        matches_pattern!(&AStruct { int: 123, string: ref "123", option: Some(123) })
+    )
+}
diff --git a/crates/googletest/tests/no_color_test.rs b/crates/googletest/tests/no_color_test.rs
index f307890..84e4cad 100644
--- a/crates/googletest/tests/no_color_test.rs
+++ b/crates/googletest/tests/no_color_test.rs
@@ -12,10 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#![cfg(feature = "supports-color")]
-
 use googletest::prelude::*;
-use indoc::indoc;
 use std::fmt::{Display, Write};
 
 // Make a long text with each element of the iterator on one line.
@@ -35,20 +32,19 @@
     std::env::set_var("NO_COLOR", "1");
     std::env::set_var("FORCE_COLOR", "1");
 
-    let result = verify_that!(build_text(1..50), eq(build_text(1..51)));
+    let result = verify_that!(build_text(1..50), eq(&build_text(1..51)));
 
     verify_that!(
         result,
-        err(displays_as(contains_substring(indoc! {
+        err(displays_as(contains_substring(
             "
-
-            Difference(-actual / +expected):
-             1
-             2
-             <---- 45 common lines omitted ---->
-             48
-             49
-            +50"
-        })))
+  Difference(-actual / +expected):
+   1
+   2
+   <---- 45 common lines omitted ---->
+   48
+   49
+  +50"
+        )))
     )
 }
diff --git a/crates/googletest/tests/pointwise_matcher_test.rs b/crates/googletest/tests/pointwise_matcher_test.rs
index cb8ef3b..b7fd7c5 100644
--- a/crates/googletest/tests/pointwise_matcher_test.rs
+++ b/crates/googletest/tests/pointwise_matcher_test.rs
@@ -18,44 +18,64 @@
 #[test]
 fn pointwise_matches_single_element() -> Result<()> {
     let value = vec![1];
-    verify_that!(value, pointwise!(lt, vec![2]))
+    verify_that!(value, pointwise!(lt, vec![&2]))
 }
 
 #[test]
 fn pointwise_matches_two_elements() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, pointwise!(lt, vec![2, 3]))
+    verify_that!(value, pointwise!(|x| points_to(lt(x)), vec![2, 3]))
+}
+
+#[test]
+fn pointwise_matches_iterator_returning_by_value() -> Result<()> {
+    #[derive(Clone, Copy, Debug)]
+    struct Countdown(i32);
+    impl Iterator for Countdown {
+        type Item = i32;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            match self.0 {
+                0 => None,
+                x => {
+                    self.0 -= 1;
+                    Some(x)
+                }
+            }
+        }
+    }
+    verify_that!(Countdown(3), pointwise!(eq, vec![3, 2, 1]))
 }
 
 #[test]
 fn pointwise_matches_two_elements_with_array() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, pointwise!(lt, [2, 3]))
+    verify_that!(value, pointwise!(lt, [&2, &3]))
 }
 
 #[test]
 fn pointwise_matches_two_element_slice() -> Result<()> {
     let value = vec![1, 2];
     let slice = value.as_slice();
-    verify_that!(*slice, pointwise!(lt, [2, 3]))
+    verify_that!(slice, pointwise!(lt, [&2, &3]))
 }
 
 #[test]
 fn pointwise_does_not_match_value_of_wrong_length() -> Result<()> {
     let value = vec![1];
-    verify_that!(value, not(pointwise!(lt, vec![2, 3])))
+    verify_that!(value, not(pointwise!(lt, vec![&2, &3])))
 }
 
 #[test]
 fn pointwise_does_not_match_value_not_matching_in_first_position() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, not(pointwise!(lt, vec![1, 3])))
+    verify_that!(value, not(pointwise!(lt, vec![&1, &3])))
 }
 
 #[test]
 fn pointwise_does_not_match_value_not_matching_in_second_position() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, not(pointwise!(lt, vec![2, 2])))
+    verify_that!(value, not(pointwise!(lt, vec![&2, &2])))
 }
 
 #[test]
@@ -64,12 +84,12 @@
         pub(super) use super::lt;
     }
     let value = vec![1];
-    verify_that!(value, pointwise!(submodule::lt, vec![2]))
+    verify_that!(value, pointwise!(submodule::lt, vec![&2]))
 }
 
 #[test]
 fn pointwise_returns_mismatch_when_actual_value_has_wrong_length() -> Result<()> {
-    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![1, 2]));
+    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![&1, &2]));
 
     verify_that!(
         result,
@@ -88,7 +108,7 @@
 
 #[test]
 fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_first_item() -> Result<()> {
-    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![2, 2, 3]));
+    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![&2, &2, &3]));
 
     verify_that!(
         result,
@@ -108,7 +128,7 @@
 
 #[test]
 fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_second_item() -> Result<()> {
-    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![1, 3, 3]));
+    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, &vec![1, 3, 3]));
 
     verify_that!(
         result,
@@ -127,9 +147,9 @@
 }
 
 #[test]
-fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_first_and_second_items()
--> Result<()> {
-    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![2, 3, 3]));
+fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_first_and_second_items(
+) -> Result<()> {
+    let result = verify_that!(vec![1, 2, 3], pointwise!(eq, &vec![2, 3, 3]));
 
     verify_that!(
         result,
@@ -150,21 +170,21 @@
 
 #[test]
 fn pointwise_matches_single_element_with_lambda_expression_with_extra_value() -> Result<()> {
-    let value = vec![1.00001f32];
+    let value = [1.00001f32];
     verify_that!(value, pointwise!(|v| near(v, 0.0001), vec![1.0]))
 }
 
 #[test]
 fn pointwise_matches_single_element_with_two_containers() -> Result<()> {
-    let value = vec![1.00001f32];
+    let value = [1.00001f32];
     verify_that!(value, pointwise!(near, vec![1.0], vec![0.0001]))
 }
 
 #[test]
 fn pointwise_matches_single_element_with_three_containers() -> Result<()> {
-    let value = vec![1.00001f32];
+    let value = [1.00001f32];
     verify_that!(
         value,
-        pointwise!(|v, t, u| near(v, t * u), vec![1.0f32], vec![0.0001f32], vec![0.5f32])
+        pointwise!(|v, t, u| near(v, t + u), vec![1.0f32], vec![0.0001f32], vec![0.5f32])
     )
 }
diff --git a/crates/googletest/tests/property_matcher_test.rs b/crates/googletest/tests/property_matcher_test.rs
index 7092446..3c9043d 100644
--- a/crates/googletest/tests/property_matcher_test.rs
+++ b/crates/googletest/tests/property_matcher_test.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::matcher::MatcherResult;
 use googletest::prelude::*;
 
 #[derive(Debug)]
@@ -41,33 +41,25 @@
 #[test]
 fn matches_struct_with_matching_property() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(SomeStruct.get_property(), eq(10)))
+    verify_that!(value, property!(&SomeStruct.get_property(), eq(10)))
 }
 
 #[test]
 fn matches_struct_with_matching_property_with_parameters() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(SomeStruct.add_product_to_field(2, 3), eq(16)))
-}
-
-#[test]
-fn matches_struct_with_matching_property_with_captured_arguments() -> Result<()> {
-    let value = SomeStruct { a_property: 10 };
-    let arg1 = 2;
-    let arg2 = 3;
-    verify_that!(value, property!(SomeStruct.add_product_to_field(arg1, arg2), eq(16)))
+    verify_that!(value, property!(&SomeStruct.add_product_to_field(2, 3), eq(16)))
 }
 
 #[test]
 fn matches_struct_with_matching_property_with_parameters_with_trailing_comma() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(SomeStruct.add_product_to_field(2, 3,), eq(16)))
+    verify_that!(value, property!(&SomeStruct.add_product_to_field(2, 3,), eq(16)))
 }
 
 #[test]
 fn matches_struct_with_matching_property_ref() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(*SomeStruct.get_property_ref(), eq(10)))
+    verify_that!(value, property!(&SomeStruct.get_property_ref(), eq(&10)))
 }
 
 #[test]
@@ -82,7 +74,7 @@
         }
     }
     let value = StructWithString { property: "Something".into() };
-    verify_that!(value, property!(*StructWithString.get_property_ref(), eq("Something")))
+    verify_that!(value, property!(&StructWithString.get_property_ref(), eq("Something")))
 }
 
 #[test]
@@ -97,31 +89,31 @@
         }
     }
     let value = StructWithVec { property: vec![1, 2, 3] };
-    verify_that!(value, property!(*StructWithVec.get_property_ref(), eq([1, 2, 3])))
+    verify_that!(value, property!(&StructWithVec.get_property_ref(), eq([1, 2, 3])))
 }
 
 #[test]
 fn matches_struct_with_matching_property_ref_with_parameters() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(*SomeStruct.get_property_ref_with_params(2, 3), eq(10)))
+    verify_that!(value, property!(&SomeStruct.get_property_ref_with_params(2, 3), eq(&10)))
 }
 
 #[test]
 fn matches_struct_with_matching_property_ref_with_parameters_and_trailing_comma() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(*SomeStruct.get_property_ref_with_params(2, 3,), eq(10)))
+    verify_that!(value, property!(&SomeStruct.get_property_ref_with_params(2, 3,), eq(&10)))
 }
 
 #[test]
 fn does_not_match_struct_with_non_matching_property() -> Result<()> {
     let value = SomeStruct { a_property: 2 };
-    verify_that!(value, not(property!(SomeStruct.get_property(), eq(1))))
+    verify_that!(value, not(property!(&SomeStruct.get_property(), eq(1))))
 }
 
 #[test]
 fn describes_itself_in_matching_case() -> Result<()> {
     verify_that!(
-        property!(SomeStruct.get_property(), eq(1)).describe(MatcherResult::Match),
+        property!(&SomeStruct.get_property(), eq(1)).describe(MatcherResult::Match),
         displays_as(eq("has property `get_property()`, which is equal to 1"))
     )
 }
@@ -129,20 +121,44 @@
 #[test]
 fn describes_itself_in_not_matching_case() -> Result<()> {
     verify_that!(
-        property!(SomeStruct.get_property(), eq(1)).describe(MatcherResult::NoMatch),
+        property!(&SomeStruct.get_property(), eq(1)).describe(MatcherResult::NoMatch),
         displays_as(eq("has property `get_property()`, which isn't equal to 1"))
     )
 }
 
 #[test]
 fn explains_mismatch_referencing_explanation_of_inner_matcher() -> Result<()> {
+    #[derive(Debug)]
+    struct SomeStruct;
+
     impl SomeStruct {
         fn get_a_collection(&self) -> Vec<u32> {
             vec![]
         }
     }
-    let value = SomeStruct { a_property: 2 };
-    let result = verify_that!(value, property!(SomeStruct.get_a_collection(), container_eq([1])));
+    let value = SomeStruct;
+    let result =
+        verify_that!(value, property!(&SomeStruct.get_a_collection(), ref container_eq([1])));
+
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(
+            "whose property `get_a_collection()` is `[]`, which is missing the element 1"
+        )))
+    )
+}
+
+#[test]
+fn explains_mismatch_referencing_explanation_of_inner_matcher_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    struct SomeStruct;
+    impl SomeStruct {
+        fn get_a_collection(&self) -> Vec<u32> {
+            vec![]
+        }
+    }
+    let result =
+        verify_that!(SomeStruct, property!(SomeStruct.get_a_collection(), container_eq([1])));
 
     verify_that!(
         result,
@@ -155,7 +171,7 @@
 #[test]
 fn describes_itself_in_matching_case_for_ref() -> Result<()> {
     verify_that!(
-        property!(*SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::Match),
+        property!(&SomeStruct.get_property_ref(), eq(&1)).describe(MatcherResult::Match),
         displays_as(eq("has property `get_property_ref()`, which is equal to 1"))
     )
 }
@@ -163,7 +179,7 @@
 #[test]
 fn describes_itself_in_not_matching_case_for_ref() -> Result<()> {
     verify_that!(
-        property!(*SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::NoMatch),
+        property!(&SomeStruct.get_property_ref(), eq(&1)).describe(MatcherResult::NoMatch),
         displays_as(eq("has property `get_property_ref()`, which isn't equal to 1"))
     )
 }
@@ -171,14 +187,16 @@
 #[test]
 fn explains_mismatch_referencing_explanation_of_inner_matcher_for_ref() -> Result<()> {
     static EMPTY_COLLECTION: Vec<u32> = vec![];
+    #[derive(Debug)]
+    struct SomeStruct;
     impl SomeStruct {
         fn get_a_collection_ref(&self) -> &[u32] {
             &EMPTY_COLLECTION
         }
     }
-    let value = SomeStruct { a_property: 2 };
+    let value = SomeStruct;
     let result =
-        verify_that!(value, property!(*SomeStruct.get_a_collection_ref(), container_eq([1])));
+        verify_that!(value, property!(&SomeStruct.get_a_collection_ref(), container_eq([1])));
 
     verify_that!(
         result,
@@ -187,3 +205,94 @@
         )))
     )
 }
+
+#[test]
+fn matches_copy_to_copy() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct Struct;
+    impl Struct {
+        fn property(self) -> i32 {
+            32
+        }
+    }
+
+    verify_that!(Struct, property!(Struct.property(), eq(32)))
+}
+
+#[test]
+fn matches_copy_to_ref() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct Struct;
+    impl Struct {
+        fn property(self) -> String {
+            "something".into()
+        }
+    }
+
+    verify_that!(Struct, property!(Struct.property(), ref eq("something")))
+}
+
+#[test]
+fn matches_copy_but_by_ref() -> Result<()> {
+    #[derive(Debug, Clone, Copy)]
+    struct Struct;
+    impl Struct {
+        fn property(&self) -> String {
+            "something".into()
+        }
+    }
+
+    verify_that!(&Struct, property!(&Struct.property(), ref eq("something")))
+}
+
+#[test]
+fn matches_ref_to_ref() -> Result<()> {
+    #[derive(Debug)]
+    struct Struct;
+    impl Struct {
+        fn property(&self) -> String {
+            "something".into()
+        }
+    }
+
+    verify_that!(Struct, property!(&Struct.property(), ref eq("something")))
+}
+
+#[test]
+fn matches_ref_to_copy() -> Result<()> {
+    #[derive(Debug)]
+    struct Struct;
+    impl Struct {
+        fn property(&self) -> i32 {
+            32
+        }
+    }
+
+    verify_that!(Struct, property!(&Struct.property(), eq(32)))
+}
+
+#[test]
+fn matches_ref_to_ref_with_binding_mode() -> Result<()> {
+    #[derive(Debug)]
+    struct Struct;
+    impl Struct {
+        fn property(&self) -> String {
+            "something".into()
+        }
+    }
+
+    verify_that!(Struct, property!(Struct.property(), eq("something")))
+}
+
+#[test]
+fn matches_property_auto_eq() -> Result<()> {
+    #[derive(Debug)]
+    struct Struct;
+    impl Struct {
+        fn property(&self) -> String {
+            "something".into()
+        }
+    }
+
+    verify_that!(Struct, property!(Struct.property(), "something"))
+}
diff --git a/crates/googletest/tests/tuple_matcher_test.rs b/crates/googletest/tests/tuple_matcher_test.rs
index a93ffee..a19e6fd 100644
--- a/crates/googletest/tests/tuple_matcher_test.rs
+++ b/crates/googletest/tests/tuple_matcher_test.rs
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::matcher::MatcherResult;
 use googletest::prelude::*;
 use indoc::indoc;
 
@@ -22,6 +22,12 @@
 }
 
 #[test]
+fn empty_matcher_matches_empty_tuple_reference() -> Result<()> {
+    let a_ok: std::result::Result<(), String> = Ok(()); // Non copy
+    verify_that!(a_ok, ok(()))
+}
+
+#[test]
 fn singleton_matcher_matches_matching_singleton_tuple() -> Result<()> {
     verify_that!((123,), (eq(123),))
 }
@@ -176,7 +182,7 @@
 #[test]
 fn tuple_matcher_1_has_correct_description_for_match() -> Result<()> {
     verify_that!(
-        (eq::<i32, _>(1),).describe(MatcherResult::Match),
+        Matcher::<(i32,)>::describe(&(eq(1),), MatcherResult::Match),
         displays_as(eq(indoc!(
             "
             is a tuple whose values respectively match:
@@ -188,7 +194,7 @@
 #[test]
 fn tuple_matcher_1_has_correct_description_for_mismatch() -> Result<()> {
     verify_that!(
-        (eq::<i32, _>(1),).describe(MatcherResult::NoMatch),
+        Matcher::<(i32,)>::describe(&(eq(1),), MatcherResult::NoMatch),
         displays_as(eq(indoc!(
             "
             is a tuple whose values do not respectively match:
@@ -200,7 +206,7 @@
 #[test]
 fn tuple_matcher_2_has_correct_description_for_match() -> Result<()> {
     verify_that!(
-        (eq::<i32, _>(1), eq::<i32, _>(2)).describe(MatcherResult::Match),
+        Matcher::<(i32, i32)>::describe(&(eq(1), eq(2)), MatcherResult::Match),
         displays_as(eq(indoc!(
             "
             is a tuple whose values respectively match:
@@ -213,7 +219,7 @@
 #[test]
 fn tuple_matcher_2_has_correct_description_for_mismatch() -> Result<()> {
     verify_that!(
-        (eq::<i32, _>(1), eq::<i32, _>(2)).describe(MatcherResult::NoMatch),
+        Matcher::<(i32, i32)>::describe(&(eq(1), eq(2)), MatcherResult::NoMatch),
         displays_as(eq(indoc!(
             "
             is a tuple whose values do not respectively match:
@@ -226,7 +232,7 @@
 #[test]
 fn describe_match_shows_which_tuple_element_did_not_match() -> Result<()> {
     verify_that!(
-        (eq(1), eq(2)).explain_match(&(1, 3)),
+        (eq(1), eq(2)).explain_match((1, 3)),
         displays_as(eq(indoc!(
             "
             which
@@ -242,7 +248,7 @@
 #[test]
 fn describe_match_shows_which_two_tuple_elements_did_not_match() -> Result<()> {
     verify_that!(
-        (eq(1), eq(2)).explain_match(&(2, 3)),
+        (eq(1), eq(2)).explain_match((2, 3)),
         displays_as(eq(indoc!(
             "
             which
diff --git a/crates/googletest/tests/unordered_elements_are_matcher_test.rs b/crates/googletest/tests/unordered_elements_are_matcher_test.rs
index bd61417..0678b0c 100644
--- a/crates/googletest/tests/unordered_elements_are_matcher_test.rs
+++ b/crates/googletest/tests/unordered_elements_are_matcher_test.rs
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use googletest::matcher::Matcher;
 use googletest::prelude::*;
 use indoc::indoc;
 use std::collections::HashMap;
@@ -32,20 +31,40 @@
 #[test]
 fn unordered_elements_are_matches_vector() -> Result<()> {
     let value = vec![1, 2, 3];
-    verify_that!(value, unordered_elements_are![eq(1), eq(2), eq(3)])
+    verify_that!(value, unordered_elements_are![eq(&1), eq(&2), eq(&3)])
+}
+
+#[test]
+fn unordered_elements_are_matches_iterator_returning_by_value() -> Result<()> {
+    #[derive(Debug, Copy, Clone)]
+    struct Countdown(i32);
+    impl Iterator for Countdown {
+        type Item = i32;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            match self.0 {
+                0 => None,
+                x => {
+                    self.0 -= 1;
+                    Some(x)
+                }
+            }
+        }
+    }
+    verify_that!(Countdown(3), unordered_elements_are![eq(1), eq(2), eq(3)])
 }
 
 #[test]
 fn unordered_elements_are_omitted() -> Result<()> {
     let value = vec![1, 2, 3];
-    verify_that!(value, {eq(3), eq(2), eq(1)})
+    verify_that!(value, {eq(&3), eq(&2), eq(&1)})
 }
 
 #[test]
 fn unordered_elements_are_matches_slice() -> Result<()> {
     let value = vec![1, 2, 3];
     let slice = value.as_slice();
-    verify_that!(*slice, unordered_elements_are![eq(1), eq(2), eq(3)])
+    verify_that!(slice, unordered_elements_are![eq(&1), eq(&2), eq(&3)])
 }
 
 #[test]
@@ -53,7 +72,7 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
     verify_that!(
         value,
-        unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
+        unordered_elements_are![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three"))]
     )
 }
 
@@ -62,7 +81,7 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
     verify_that!(
         value,
-        unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")),]
+        unordered_elements_are![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")),]
     )
 }
 
@@ -71,7 +90,11 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (4, "Three")]);
     verify_that!(
         value,
-        not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+        not(unordered_elements_are![
+            (eq(&2), eq(&"Two")),
+            (eq(&1), eq(&"One")),
+            (eq(&3), eq(&"Three"))
+        ])
     )
 }
 
@@ -80,7 +103,11 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Four")]);
     verify_that!(
         value,
-        not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+        not(unordered_elements_are![
+            (eq(&2), eq(&"Two")),
+            (eq(&1), eq(&"One")),
+            (eq(&3), eq(&"Three"))
+        ])
     )
 }
 
@@ -89,14 +116,18 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two")]);
     verify_that!(
         value,
-        not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+        not(unordered_elements_are![
+            (eq(&2), eq(&"Two")),
+            (eq(&1), eq(&"One")),
+            (eq(&3), eq(&"Three"))
+        ])
     )
 }
 
 #[test]
 fn unordered_elements_are_does_not_match_hash_map_with_extra_element() -> Result<()> {
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
-    verify_that!(value, not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One"))]))
+    verify_that!(value, not(unordered_elements_are![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One"))]))
 }
 
 #[test]
@@ -104,20 +135,24 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Three"), (3, "Two")]);
     verify_that!(
         value,
-        not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+        not(unordered_elements_are![
+            (eq(&2), eq(&"Two")),
+            (eq(&1), eq(&"One")),
+            (eq(&3), eq(&"Three"))
+        ])
     )
 }
 
 #[test]
 fn unordered_elements_are_matches_vector_with_trailing_comma() -> Result<()> {
     let value = vec![1, 2, 3];
-    verify_that!(value, unordered_elements_are![eq(1), eq(2), eq(3),])
+    verify_that!(value, unordered_elements_are![eq(&1), eq(&2), eq(&3),])
 }
 
 #[test]
 fn unordered_elements_are_matches_size() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, not(unordered_elements_are![eq(1), eq(2), eq(3)]))
+    verify_that!(value, not(unordered_elements_are![eq(&1), eq(&2), eq(&3)]))
 }
 
 #[test]
@@ -125,7 +160,7 @@
     #[derive(Debug, PartialEq)]
     struct AStruct(i32);
     let expected_value = AStruct(123);
-    verify_that!(vec![AStruct(123)], unordered_elements_are![eq_deref_of(&expected_value)])
+    verify_that!(vec![AStruct(123)], unordered_elements_are![eq(&expected_value)])
 }
 
 #[test]
@@ -135,13 +170,13 @@
     let expected_value = AStruct(123);
     verify_that!(
         HashMap::from([(1, AStruct(123))]),
-        unordered_elements_are![(eq(1), eq_deref_of(&expected_value))]
+        unordered_elements_are![(eq(&1), eq(&expected_value))]
     )
 }
 
 #[test]
 fn unordered_elements_are_description_mismatch() -> Result<()> {
-    let result = verify_that!(vec![1, 4, 3], unordered_elements_are![eq(1), eq(2), eq(3)]);
+    let result = verify_that!(vec![1, 4, 3], unordered_elements_are![eq(&1), eq(&2), eq(&3)]);
     verify_that!(
         result,
         err(displays_as(contains_substring(indoc!(
@@ -160,27 +195,29 @@
 #[test]
 fn unordered_elements_are_matches_unordered() -> Result<()> {
     let value = vec![1, 2];
-    verify_that!(value, unordered_elements_are![eq(2), eq(1)])
+    verify_that!(value, unordered_elements_are![eq(&2), eq(&1)])
 }
 
 #[test]
 fn unordered_elements_are_matches_unordered_with_repetition() -> Result<()> {
     let value = vec![1, 2, 1, 2, 1];
-    verify_that!(value, unordered_elements_are![eq(1), eq(1), eq(1), eq(2), eq(2)])
+    verify_that!(value, unordered_elements_are![eq(&1), eq(&1), eq(&1), eq(&2), eq(&2)])
 }
 
 #[test]
 fn unordered_elements_are_explains_mismatch_due_to_wrong_size() -> Result<()> {
+    let matcher = unordered_elements_are![eq(&2), eq(&3), eq(&4)];
     verify_that!(
-        unordered_elements_are![eq(2), eq(3), eq(4)].explain_match(&vec![2, 3]),
+        matcher.explain_match(&vec![2, 3]),
         displays_as(eq("which has size 2 (expected 3)"))
     )
 }
 
 #[test]
 fn unordered_elements_are_description_no_full_match() -> Result<()> {
+    let matcher = unordered_elements_are![eq(&1), eq(&2), eq(&2)];
     verify_that!(
-        unordered_elements_are![eq(1), eq(2), eq(2)].explain_match(&vec![1, 1, 2]),
+        matcher.explain_match(&vec![1, 1, 2]),
         displays_as(eq(indoc!(
             "
             which does not have a perfect match with the expected elements. The best match found was:
@@ -194,22 +231,24 @@
 
 #[test]
 fn unordered_elements_are_unmatchable_expected_description_mismatch() -> Result<()> {
+    let matcher = unordered_elements_are![eq(&1), eq(&2), eq(&3)];
     verify_that!(
-        unordered_elements_are![eq(1), eq(2), eq(3)].explain_match(&vec![1, 1, 3]),
+        matcher.explain_match(&vec![1, 1, 3]),
         displays_as(eq("which has no element matching the expected element #1"))
     )
 }
 
 #[test]
 fn unordered_elements_are_unmatchable_actual_description_mismatch() -> Result<()> {
+    let matcher = unordered_elements_are![eq(&1), eq(&1), eq(&3)];
     verify_that!(
-        unordered_elements_are![eq(1), eq(1), eq(3)].explain_match(&vec![1, 2, 3]),
+        matcher.explain_match(&vec![1, 2, 3]),
         displays_as(eq("whose element #1 does not match any expected elements"))
     )
 }
 
-fn create_matcher() -> impl Matcher<ActualT = Vec<i32>> {
-    unordered_elements_are![eq(1)]
+fn create_matcher<'a>() -> impl Matcher<&'a Vec<i32>> {
+    unordered_elements_are![eq(&1)]
 }
 
 #[test]
@@ -217,8 +256,8 @@
     verify_that!(vec![1], create_matcher())
 }
 
-fn create_matcher_for_map() -> impl Matcher<ActualT = HashMap<i32, i32>> {
-    unordered_elements_are![(eq(1), eq(1))]
+fn create_matcher_for_map<'a>() -> impl Matcher<&'a HashMap<i32, i32>> {
+    unordered_elements_are![(eq(&1), eq(&1))]
 }
 
 #[test]
@@ -228,24 +267,24 @@
 
 #[test]
 fn contains_each_matches_when_one_to_one_correspondence_present() -> Result<()> {
-    verify_that!(vec![2, 3, 4], contains_each!(eq(2), eq(3), eq(4)))
+    verify_that!(vec![2, 3, 4], contains_each!(eq(&2), eq(&3), eq(&4)))
 }
 
 #[test]
 fn contains_each_supports_trailing_comma() -> Result<()> {
-    verify_that!(vec![2, 3, 4], contains_each!(eq(2), eq(3), eq(4),))
+    verify_that!(vec![2, 3, 4], contains_each!(eq(&2), eq(&3), eq(&4),))
 }
 
 #[test]
 fn contains_each_matches_hash_map() -> Result<()> {
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
-    verify_that!(value, contains_each![(eq(2), eq("Two")), (eq(1), eq("One"))])
+    verify_that!(value, contains_each![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One"))])
 }
 
 #[test]
 fn contains_each_matches_hash_map_with_trailing_comma() -> Result<()> {
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
-    verify_that!(value, contains_each![(eq(2), eq("Two")), (eq(1), eq("One")),])
+    verify_that!(value, contains_each![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")),])
 }
 
 #[test]
@@ -265,42 +304,46 @@
 
 #[test]
 fn contains_each_matches_when_excess_elements_present() -> Result<()> {
-    verify_that!(vec![1, 2, 3, 4], contains_each!(eq(2), eq(3), eq(4)))
+    verify_that!(vec![1, 2, 3, 4], contains_each!(eq(&2), eq(&3), eq(&4)))
 }
 
 #[test]
 fn contains_each_does_not_match_when_matchers_are_unmatched() -> Result<()> {
-    verify_that!(vec![1, 2, 3], not(contains_each!(eq(2), eq(3), eq(4))))
+    verify_that!(vec![1, 2, 3], not(contains_each!(eq(&2), eq(&3), eq(&4))))
 }
 
 #[test]
 fn contains_each_explains_mismatch_due_to_wrong_size() -> Result<()> {
+    let matcher = contains_each![eq(&2), eq(&3), eq(&4)];
     verify_that!(
-        contains_each![eq(2), eq(3), eq(4)].explain_match(&vec![2, 3]),
+        matcher.explain_match(&vec![2, 3]),
         displays_as(eq("which has size 2 (expected at least 3)"))
     )
 }
 
 #[test]
 fn contains_each_explains_missing_element_in_mismatch() -> Result<()> {
+    let matcher = contains_each![eq(&2), eq(&3), eq(&4)];
     verify_that!(
-        contains_each![eq(2), eq(3), eq(4)].explain_match(&vec![1, 2, 3]),
+        matcher.explain_match(&vec![1, 2, 3]),
         displays_as(eq("which has no element matching the expected element #2"))
     )
 }
 
 #[test]
 fn contains_each_explains_missing_elements_in_mismatch() -> Result<()> {
+    let matcher = contains_each![eq(&2), eq(&3), eq(&4), eq(&5)];
     verify_that!(
-        contains_each![eq(2), eq(3), eq(4), eq(5)].explain_match(&vec![0, 1, 2, 3]),
+        matcher.explain_match(&vec![0, 1, 2, 3]),
         displays_as(eq("which has no elements matching the expected elements #2, #3"))
     )
 }
 
 #[test]
 fn contains_each_explains_mismatch_due_to_no_graph_matching_found() -> Result<()> {
+    let matcher = contains_each![ge(&2), ge(&2)];
     verify_that!(
-        contains_each![ge(2), ge(2)].explain_match(&vec![1, 2]),
+        matcher .explain_match(&vec![1, 2]),
         displays_as(eq(indoc!(
             "
             which does not have a superset match with the expected elements. The best match found was:
@@ -324,12 +367,12 @@
 
 #[test]
 fn is_contained_in_matches_when_one_to_one_correspondence_present() -> Result<()> {
-    verify_that!(vec![2, 3, 4], is_contained_in!(eq(2), eq(3), eq(4)))
+    verify_that!(vec![2, 3, 4], is_contained_in!(eq(&2), eq(&3), eq(&4)))
 }
 
 #[test]
 fn is_contained_supports_trailing_comma() -> Result<()> {
-    verify_that!(vec![2, 3, 4], is_contained_in!(eq(2), eq(3), eq(4),))
+    verify_that!(vec![2, 3, 4], is_contained_in!(eq(&2), eq(&3), eq(&4),))
 }
 
 #[test]
@@ -337,7 +380,7 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two")]);
     verify_that!(
         value,
-        is_contained_in![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
+        is_contained_in![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three"))]
     )
 }
 
@@ -346,53 +389,57 @@
     let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two")]);
     verify_that!(
         value,
-        is_contained_in![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")),]
+        is_contained_in![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")),]
     )
 }
 
 #[test]
 fn is_contained_in_matches_when_container_is_empty() -> Result<()> {
-    verify_that!(vec![], is_contained_in!(eq::<i32, _>(2), eq(3), eq(4)))
+    verify_that!(vec![1; 0], is_contained_in!(eq(&2), eq(&3), eq(&4)))
 }
 
 #[test]
 fn is_contained_in_matches_when_excess_matchers_present() -> Result<()> {
-    verify_that!(vec![3, 4], is_contained_in!(eq(2), eq(3), eq(4)))
+    verify_that!(vec![3, 4], is_contained_in!(eq(&2), eq(&3), eq(&4)))
 }
 
 #[test]
 fn is_contained_in_does_not_match_when_elements_are_unmatched() -> Result<()> {
-    verify_that!(vec![1, 2, 3], not(is_contained_in!(eq(2), eq(3), eq(4))))
+    verify_that!(vec![1, 2, 3], not(is_contained_in!(eq(&2), eq(&3), eq(&4))))
 }
 
 #[test]
 fn is_contained_in_explains_mismatch_due_to_wrong_size() -> Result<()> {
+    let matcher = is_contained_in![eq(&2), eq(&3)];
     verify_that!(
-        is_contained_in![eq(2), eq(3)].explain_match(&vec![2, 3, 4]),
+        matcher.explain_match(&vec![2, 3, 4]),
         displays_as(eq("which has size 3 (expected at most 2)"))
     )
 }
 
 #[test]
 fn is_contained_in_explains_missing_element_in_mismatch() -> Result<()> {
+    let matcher = is_contained_in![eq(&2), eq(&3), eq(&4)];
     verify_that!(
-        is_contained_in![eq(2), eq(3), eq(4)].explain_match(&vec![1, 2, 3]),
+        matcher.explain_match(&vec![1, 2, 3]),
         displays_as(eq("whose element #0 does not match any expected elements"))
     )
 }
 
 #[test]
 fn is_contained_in_explains_missing_elements_in_mismatch() -> Result<()> {
+    let matcher = is_contained_in![eq(&2), eq(&3), eq(&4), eq(&5)];
     verify_that!(
-        is_contained_in![eq(2), eq(3), eq(4), eq(5)].explain_match(&vec![0, 1, 2, 3]),
+        matcher.explain_match(&vec![0, 1, 2, 3]),
         displays_as(eq("whose elements #0, #1 do not match any expected elements"))
     )
 }
 
 #[test]
 fn is_contained_in_explains_mismatch_due_to_no_graph_matching_found() -> Result<()> {
+    let matcher = is_contained_in![ge(&1), ge(&3)];
     verify_that!(
-        is_contained_in![ge(1), ge(3)].explain_match(&vec![1, 2]),
+        matcher.explain_match(&vec![1, 2]),
         displays_as(eq(indoc!(
             "
             which does not have a subset match with the expected elements. The best match found was:
@@ -401,3 +448,18 @@
               Expected element `is greater than or equal to 3` at index 1 did not match any remaining actual element."))
     ))
 }
+
+#[test]
+fn unordered_elements_are_with_auto_eq() -> Result<()> {
+    verify_that!(vec![3, 4, 2], unordered_elements_are![&2, &3, &4])
+}
+
+#[test]
+fn contains_each_with_auto_eq() -> Result<()> {
+    verify_that!(vec![3, 4, 2], contains_each![&2, &4])
+}
+
+#[test]
+fn is_contained_in_with_auto_eq() -> Result<()> {
+    verify_that!(vec![3, 4, 2], is_contained_in![&1, &2, &3, &4])
+}
diff --git a/crates/toml_edit/.android-checksum.json b/crates/toml_edit/.android-checksum.json
index 730d130..b7d1fa1 100644
--- a/crates/toml_edit/.android-checksum.json
+++ b/crates/toml_edit/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"329e35ecd896d09bda49eac82f30868b31f8f7db8731337fdffc55df47e0dd2e","Android.bp":"65b157f77aac862656a69a3847e9c2d904f456ece25092ef245f97cd404d5a3d","Cargo.lock":"96482e0c70a01d05cf76f1d99ae72605e092636d528f24ed6eb8d968c9703706","Cargo.toml":"6666bf68027e397ead9bace0003e838e5f839c29779345feae76e2e8b2e8ba8b","LICENSE":"7b1c7a1aa99d919e93ff9b6a265d75f2aefea4ae27147ab6dafd8513e64b8a9b","LICENSE-APACHE":"e4493d0badd82c469fd611cf0c31ea8a74cce85c52c4a4c2579e344226f0602e","LICENSE-MIT":"538f704e6bc384de6dd775d81e9df89f9d718f0e2808a24a789eaa03beb129d3","METADATA":"dc3920e8d39660b7311cece01b9af32dfe3f0adf30d0b9f76516d625ab9703cd","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"f45dac46b26743d4f8fe1358b5c4707f782eb6e487f8a9b8443ef774e9483b78","cargo_embargo.json":"397647bf02fb37ea9b3ec5313148fac22602f64f91ec8ee36cc82f1f0f58b793","examples/visit.rs":"4ae8ea719b969a91152fcd50800abf123e9d2f2048dd642dbad62081f510354c","patches/LICENSE.patch":"4ba5ca15afd2172bc25ec72dbac0b97c03f67beea425aa56194acd0472acf72f","src/array.rs":"c35c9cf032afa43b38f62f4a76e85eaf7ff16f3c556f5e03771380e6a41c4cb5","src/array_of_tables.rs":"65235b68cbc66098f4f2e04dbb96a1f3b756e9c931e7dccd33f63c056ea5da61","src/de/array.rs":"21c3db77683175e48df5167666b0968f80525da90dd7bd9873edf178111827e2","src/de/datetime.rs":"adcfa9c96a655face16212d4b9be0221723c5916dd4ba0b967c363273c6acf3b","src/de/key.rs":"aea799be51703266bf97b967abb77c171c91e37ea4f268ca5027bbf76382abc1","src/de/mod.rs":"f5725a351c9156fc39292979e7d875f30a8ecdb92a64c2c33a514afc0f25de9c","src/de/spanned.rs":"12f7f11ffae52d147dfd6b8e31837d28736cf88d44c93a8071eca736b26414b5","src/de/table.rs":"321a794b922ffe15ace56c9411131d088c85eb257a15923da7b3888492491504","src/de/table_enum.rs":"ad5e7916066904191bd278ed5b7013da7c7d0f50dbf243b9eda5c12129d1e8f5","src/de/value.rs":"31dab335e11ecd24fea2b4cad76bf276ac853b20bf5af59031fc27df85b15466","src/document.rs":"db2e7bda93ce2383207016ea41ab985108d83020c42eba526a898dc5d345a526","src/encode.rs":"918b36fe0d55f74613c92441e58cab3290543ea62c3ca30c75e0fbad58b8fbbe","src/error.rs":"61746a768e80db9af11a35e7586a5448bea9dd7d274cb7e19b920c726f1d3e80","src/index.rs":"73358e21f0cd9f9b96aa66160ebf5f851213c65e0d10aab9964c22b1175b44a6","src/inline_table.rs":"d816f7bfe3fbd4dd5206d789310f2d318673b6a634240be78d0e1abdd0519a73","src/internal_string.rs":"5311aef53babffa83f165f6abff62906743cb5f3fd49ad2a9871b524b35efc34","src/item.rs":"300a3c9d7f6f9875afc751f626f3da0166c63dc10b2f5481d8e70dbd8900350a","src/key.rs":"9407184017faf2d302551fa835c4fae118beaef938c15579c4f42c15ee052928","src/lib.rs":"b44a4895f15ea70055c5e018bcb9abc6bede2474e1cd9d0ad3d02f5ef450ade2","src/parser/array.rs":"bd193ac7a919fab17fc90c42a1fc18f4b6dabdcd4e228c9cfb037d4c40bc0baf","src/parser/datetime.rs":"2d5240cf5fcff04248b38047031512fa87b55d653f90f3c3d97c7c9e26308791","src/parser/document.rs":"c13e3e81248137ae34205ae344fe58b20f5a73b74f9fed8ec96dc16609825651","src/parser/error.rs":"e31e69f00dc9b642713886876573a0e55b438b20a1502481ea9ea26ccb14186a","src/parser/inline_table.rs":"f2a304861a9227ab41230154d6d22ba80f4b27deb0eab0a83881ca8c82f0a8b1","src/parser/key.rs":"196fb90f64f763495a295f196c8977396481ed524cdef5fb01fce17b27a03fb2","src/parser/mod.rs":"895db2d53480cdeee6ff9a05c4335dbd8749b554b5151f7d4565f29f4e1261e5","src/parser/numbers.rs":"879a8886d4b331b9c990b03379d60c56d851ad7e9a405a85f0fd704228ac24ba","src/parser/state.rs":"80dbeb5332631dbd514335101d90f20043424b6578f344f0220febc8c29c75ab","src/parser/strings.rs":"177996abf178e2a3a9a52f030360b6978fb694edfb74a3fdddf45e4c903aade8","src/parser/table.rs":"ebe15adb0b447c6590cde2a0726a35450b2bc24cd348b6c1199fdb4cdb434a0d","src/parser/trivia.rs":"834113d29709da90a3b4613c3745442e989ecce21eab70927374e496cbd34ba7","src/parser/value.rs":"6dd38eeaa08157d6c4aa9850ca5686b4112e59f60db3ffc801bc66378d367bac","src/raw_string.rs":"2f1edfcc331d953f6c37239afed6662a95f2fde3b9d60e5f9ec651d50232b33a","src/repr.rs":"29f13d204680430b97e7910ff2d739e48a8cd845d0ab9789bbe33f990bff3a6e","src/ser/array.rs":"c6e0e1d9b397ce3d4b9759e1b1641aadc37da00dccc45bffb9cd946c7beff544","src/ser/key.rs":"52b4d38748bef6e624764eeba7769af5be3eda1ad7be46073672e34478fefecc","src/ser/map.rs":"8305fefda54ce44bad5fc533791a1fa2b505200582c1b11c7b22a547c1d4662d","src/ser/mod.rs":"3ebafea1db5b436ae99e118bfae36c952d2f96fa8ca803d252c7df7f64d80182","src/ser/pretty.rs":"87228866743a55074d9b3d91bea68a654844621a34f81bd07beaaa652802dde0","src/ser/value.rs":"7773bdf799ab1f908ad12213c6175845ce21d36203de29817ae54a2736f1b05b","src/table.rs":"19d8877d0dd67217cfc318e1a721286b4a12727c823cf660eddad517188094af","src/value.rs":"e97a419a574c08a88728885568a07dd4c632a510895d8d6fcf088298d48c6ee7","src/visit.rs":"83e1fd0405d267883552d3cb8185bc116dc77ab1acdc8d32c8b8b4d604bc9066","src/visit_mut.rs":"00a45b66929c1561ab5b465688a0b970b066f7552dcd4b882b952b31c12fc80e","tests/decoder.rs":"0384e647759ed1e12f2f2c611d40fdcdc1d7f9a16d1fbf1f59372c3599f66034","tests/decoder_compliance.rs":"74c36d82d52205c3490844ef3df952e294aa92c6e9ee64dff231473072b78b1d","tests/encoder.rs":"8dabaa51a6c4e680c844630d2ace7a0fdffa607f8a306f661c3e7dec154f9b28","tests/encoder_compliance.rs":"1d9538ee077af51782213cc3484de5252b8700e242b832735c401fd6343623d6","tests/fixtures/invalid/array/array.stderr":"517c397c7afb8fe855fe9b86a253f620f761b5403fdbb08b219c893f096804c2","tests/fixtures/invalid/array/double-comma-1.stderr":"517c397c7afb8fe855fe9b86a253f620f761b5403fdbb08b219c893f096804c2","tests/fixtures/invalid/array/double-comma-2.stderr":"f8a4c678e4fa5bb4b2dc66064d113bd81beb5e5258046685f967feefa825e8a8","tests/fixtures/invalid/array/extend-defined-aot.stderr":"86cc5eb992fa9add3ad39766d6c6736d30dfd9c464cc516c45dec12bc1c8e8b0","tests/fixtures/invalid/array/extending-table.stderr":"9d666931632f0c02362f102e9edd86916a7de491a83d6dfc68a0c2f87b0794d6","tests/fixtures/invalid/array/missing-separator-1.stderr":"fb21970f88b6f5ede63891c6baf406cfcefea7d48c15c0b4670c71d97c61c3ce","tests/fixtures/invalid/array/missing-separator-2.stderr":"3a807ad6e94c3d70b62696bec39d7a1e558e5771d6c6484a80e89ffde6aa9540","tests/fixtures/invalid/array/no-close-1.stderr":"7c1689bf4779e193a475bc181b622e2dcaa2b02e2c9a052ba953c94d2e48b724","tests/fixtures/invalid/array/no-close-2.stderr":"e3421314255375d50d74e04b527adec9aa1b7cd9a8502120247785a34f0b7368","tests/fixtures/invalid/array/no-close-3.stderr":"ec1d919de5766216b887a34099b2117db7e3e3cfaccd8b295f44268d950df14e","tests/fixtures/invalid/array/no-close-4.stderr":"4372b71512f369cefea355dc044e04483c5f7943028ad5cc0d3514804d3ad18d","tests/fixtures/invalid/array/no-close-5.stderr":"87d9fcd6aae71e0542f70eb8ee2a509f024e66d0b10d03b2f7f697d76b2bb575","tests/fixtures/invalid/array/no-close-6.stderr":"9ae5dc0d71db715990ef8c7e8ded74643c4a451c5abb4c05fe1bd6eae61d20b9","tests/fixtures/invalid/array/no-close-7.stderr":"eae2afb173b6a4d58e65dc94782c1b5079588806396c6b5716fefe911643966a","tests/fixtures/invalid/array/no-close-8.stderr":"feba7ed79aaa5d7b92fca24d09ddfdc1a2198e84a51aa8a4a7578b75e945e002","tests/fixtures/invalid/array/no-close-table-1.stderr":"ff5517cc98a1e0918889defcf594a919f1c52ef508bcc4183114305f2df8cd77","tests/fixtures/invalid/array/no-close-table-2.stderr":"21751532b65a2db6064cee09f15f65d4773c34ed83b7269d09e2ac00e5859278","tests/fixtures/invalid/array/no-comma-1.stderr":"02a1e51cbf040add31472bd7c56723005c89c8b793ef59769cad78d36e4f3139","tests/fixtures/invalid/array/no-comma-2.stderr":"4dc26ed513ceb32d721c4e25a7a1918b9c61bc93d926bbab04975dc1a930fda3","tests/fixtures/invalid/array/no-comma-3.stderr":"f5441aa4804a2ce10fa621304def1c296b4b35bf6e7657a2926d364b9d3d4db9","tests/fixtures/invalid/array/only-comma-1.stderr":"b7d5fe39fb03327a5b55b940f524aa4dbb5548604fbc8786e1e4e502fbf6f16c","tests/fixtures/invalid/array/only-comma-2.stderr":"4a54ddd36885beb51fa70327ecfe81fd6dddc5f1f23ff85a61f30834577b4b2a","tests/fixtures/invalid/array/tables-1.stderr":"2a9ec07bfa4c818d204e8d49c621e13194cffc99c21a75533a8dd3b00ffcfdb2","tests/fixtures/invalid/array/tables-2.stderr":"e04375e46225e12956b22fb717615b6cf33f2064f12a9a6c93074f42adaa5374","tests/fixtures/invalid/array/text-after-array-entries.stderr":"320eb4e7f8ba560da84cac292a9d5de03534f6ca6ff6928e429ae8cd55ec4395","tests/fixtures/invalid/array/text-before-array-separator.stderr":"b37ba17334486b7b16d4b5bde308d2a8c7605429c389e3ec30b02cd975fcf441","tests/fixtures/invalid/array/text-in-array.stderr":"895f20e5d6f2748c16659edbdb4c6e91d84f29e45f8e72d380bb3205f6c9065a","tests/fixtures/invalid/bool/almost-false-with-extra.stderr":"9c06a48312aa0138684e37b8d4193e46ff1badf6ee3d2424d6c6e5f341db8a61","tests/fixtures/invalid/bool/almost-false.stderr":"d3c827c1c521f98b2242a7183c96b00682764889a46666a5dcfb6d0fa7a743c0","tests/fixtures/invalid/bool/almost-true-with-extra.stderr":"9db6045a6e3ba501874de810a16a35f9837647dbab22ea9d8c9d89a7494b983f","tests/fixtures/invalid/bool/almost-true.stderr":"2816a9e9637d3402750f7a35622d738abc83825d706fb4c19d8f007df2396bb8","tests/fixtures/invalid/bool/bool.stderr":"9c06a48312aa0138684e37b8d4193e46ff1badf6ee3d2424d6c6e5f341db8a61","tests/fixtures/invalid/bool/capitalized-false.stderr":"b7e9c3764103c257ceae4f0aeb714ae58917722cb6ac1a1fdb0cde6f9efe1c83","tests/fixtures/invalid/bool/capitalized-true.stderr":"ac46ab983618490106a1bd3263e0e34e54010a014a795184fb722d5ef49aab2d","tests/fixtures/invalid/bool/just-f.stderr":"d3f2bc776b3137f378be5acb9e10e745cd124178d836289fb1b7091ab12556fb","tests/fixtures/invalid/bool/just-t.stderr":"1f6c7ced5fd72596cde8daf3bd05a61c9f0a28bfb612b8b6b0b6de4630d1c0a0","tests/fixtures/invalid/bool/mixed-case-false.stderr":"7d57131577e1c018075c1a83347e80183aa1db1b6aee1027a11cb7f229b05d21","tests/fixtures/invalid/bool/mixed-case-true.stderr":"9cfe3868f18cecce20bc674593f15fe88cb6fb7361f54ffec1616e877f714cf2","tests/fixtures/invalid/bool/mixed-case.stderr":"34738e926e3e5ea99fc9860c0177463699e54b2c48fe11168a6c25bc173802ac","tests/fixtures/invalid/bool/starting-same-false.stderr":"620c142ae095c8da9aa1298a82a42ce1f0f13eda91c66e75e4c5b6f82bee0455","tests/fixtures/invalid/bool/starting-same-true.stderr":"8e1016111cd84601db76df8579ce7aab7fa7a0e013e66d3174cb8bbd80c4764f","tests/fixtures/invalid/bool/wrong-case-false.stderr":"ca86c0166b4992d0255e2c8f5ed2935d58c325965039006f1903c8ddaf549593","tests/fixtures/invalid/bool/wrong-case-true.stderr":"6264fc52c455d04e1f89d0d2726b4325066699595f6120ad4583802642cd6735","tests/fixtures/invalid/control/bare-cr.stderr":"be847ba07cd507752b8124ca13e8d012c666b81cee56be1309815c8d490470c1","tests/fixtures/invalid/control/bare-formfeed.stderr":"18a939f8d8459a4e3a2d9dee5756fa81b3f12a2179e685c35ecd25f4da9d8aef","tests/fixtures/invalid/control/bare-null.stderr":"cd58586e56fa9b00fa17be23dfd531dcf9eb5e10556464decd4b60fdc6eaf72f","tests/fixtures/invalid/control/bare-vertical-tab.stderr":"970f649f9575b1553da1382466eafe16413696203e7ddbaa99a0585a3f0ca157","tests/fixtures/invalid/control/comment-cr.stderr":"7e729bfc9b0c575521c39d763e8ab8f7baf94054e183e59cbe976d840a0f1c62","tests/fixtures/invalid/control/comment-del.stderr":"5c41d37aad38933267d8d8fe80d9600650627dbe3433b8ac876ca63bd086094b","tests/fixtures/invalid/control/comment-ff.stderr":"4a7eaa53d883dcab7f23b09d33794a687b5d0c7777ab172a0bd81f9503878762","tests/fixtures/invalid/control/comment-lf.stderr":"a24258d06bc41156b92ab14fac3793c85d706e74d5c280337c4ec0c4f80f92ff","tests/fixtures/invalid/control/comment-null.stderr":"afcf5791d6fde718cd6a2b514483d45d2355bf08f9fa392d75c7192bc390913e","tests/fixtures/invalid/control/comment-us.stderr":"280fbd7dc323d8841d5b9fa821f48061fc01a968bc1f5893fdac30fc17afaa3d","tests/fixtures/invalid/control/control.stderr":"07adf02cb547ed379a402121e4db738daa698358e53c2fa852c617fb766b41fc","tests/fixtures/invalid/control/multi-cr.stderr":"1b70b31b5c10eac9a52cad0b7739bbe5b9a74c9cce4ba105143c622a33164c14","tests/fixtures/invalid/control/multi-del.stderr":"6a7f52409dbf8b5bc1097fec2d1330310c97d78eb3ea2027f219b25e519ec270","tests/fixtures/invalid/control/multi-lf.stderr":"e723fdaf9e19aef9a0eee8a919e5986822c25ac1131e24422e9fabc0887ccc63","tests/fixtures/invalid/control/multi-null.stderr":"ab3e944c835a74ebab8c56c39c3ba6496e8d4d2b03f324a66e2e511ac8b7fd10","tests/fixtures/invalid/control/multi-us.stderr":"1df029e738c4cb78a68b360fc131fb6d9f5b93f78e292a27aaf4c40e8aa3d842","tests/fixtures/invalid/control/rawmulti-cd.stderr":"fbadd57208404b2116c9cc16f36b80e1ab08276d7838e17e4bd48a8df7e194d2","tests/fixtures/invalid/control/rawmulti-del.stderr":"ee50d46a73bfcd85f7b3b677d4148cf2390e5ea13b59a6bc81dacea3e049ab33","tests/fixtures/invalid/control/rawmulti-lf.stderr":"6cae2d709ae5ee6afc355322388d8b57d8e9674929d71af5b7761bab5d751cb9","tests/fixtures/invalid/control/rawmulti-null.stderr":"0efaa11fc391b16d27e1de09027171e8d379d3ffddd2489045ba10912ebccb89","tests/fixtures/invalid/control/rawmulti-us.stderr":"31a28ca07db60771f7787f8ef30fa160ce3715df28532bc9892ec15e380c36c1","tests/fixtures/invalid/control/rawstring-cr.stderr":"32819db35fde8ef9ee282557740c8b2dd077c0bcf421614fb06db18d732af57e","tests/fixtures/invalid/control/rawstring-del.stderr":"8a7828be56c7d941c6879698be4825fc1f9b75766898e782675d860581e7393e","tests/fixtures/invalid/control/rawstring-lf.stderr":"1d01b15b19024ce4c36866f025ed6adc4f3c3bac1a2b5fdf4ea79471dc5c9b49","tests/fixtures/invalid/control/rawstring-null.stderr":"245b39b3943e776fb5cb2d52497e0fb301420c36f7f5b47df9980aa05496ab5b","tests/fixtures/invalid/control/rawstring-us.stderr":"7fb0ed283f42d3b1cc3852698b6047c2975d2c780eebe14f65d2b3d11d9f0bad","tests/fixtures/invalid/control/string-bs.stderr":"0a12dfbbf6161d24f29d78679928a4e835d47558344773c2d585a2201bb043ad","tests/fixtures/invalid/control/string-cr.stderr":"cf5970ede487e7fd692baa45247d55df70b8207f8d67df6c2e55eb9d4ef9352b","tests/fixtures/invalid/control/string-del.stderr":"35c65f3ceb74b80d4a5d8109f61f099bdf640379dc9751fd7678447a33eaec78","tests/fixtures/invalid/control/string-lf.stderr":"ede4b6df958cac81b05525f25b1ac2c1435eae895f545dac57a553a7840fc8d7","tests/fixtures/invalid/control/string-null.stderr":"c30aa237809c03dee02d36a1fc4a02d6f184bd425bfc2accda6b3fa1bb55fe1a","tests/fixtures/invalid/control/string-us.stderr":"7d922c62bf118c23b85991935b8b4c7a113d39d0092d13d22be1e0ad7ed20a31","tests/fixtures/invalid/datetime/feb-29.stderr":"743e7bcaa21ce6a8e7f033b56de7a0f3e7dd54ea84bf293e044af4e192f693bf","tests/fixtures/invalid/datetime/feb-30.stderr":"744e980d51f19b89a203192eae4f88e5ecba71853e006ab1c52988818584507c","tests/fixtures/invalid/datetime/hour-over.stderr":"0aabf978f79acc6b4d0d3d6a9b05833aa65d418d71aa5cbbd71035797adc1b0e","tests/fixtures/invalid/datetime/mday-over.stderr":"76f70be6d3ca444339aa15f4e6243b784a4c414fde0cd932b20c182266276782","tests/fixtures/invalid/datetime/mday-under.stderr":"54456d2ec892deb33b596cd82d400bdc6777cd2b5c643e45556b9646057f7d55","tests/fixtures/invalid/datetime/minute-over.stderr":"ffa678092cb67d6848910218d9a5bd46f5a308b7bce2269b8b6e46c2ec019dd0","tests/fixtures/invalid/datetime/month-over.stderr":"906596048b7dbbae0630ec1216827a95ce562a09f98704595d27c3491cdfc7ee","tests/fixtures/invalid/datetime/month-under.stderr":"c073c25ef0a3aa10b1d29ac28df9c9207143ffd595680ff633400d076a76dde7","tests/fixtures/invalid/datetime/no-leads-month.stderr":"4f5066ed79bcd945215a57f55ef230f47dd64e2ec55438a7ee08bc3f7067e318","tests/fixtures/invalid/datetime/no-leads-with-milli.stderr":"ffce1f8e7bb51130705ebb7ba8e9ef007de9a9e7f33b476e4fefb15a030d2e1d","tests/fixtures/invalid/datetime/no-leads.stderr":"4f5066ed79bcd945215a57f55ef230f47dd64e2ec55438a7ee08bc3f7067e318","tests/fixtures/invalid/datetime/no-secs.stderr":"8c504dfaa1dfef25b78a8548509b3139f92e3e04bcb848a1e04f7529e169ad63","tests/fixtures/invalid/datetime/no-t.stderr":"ec4852cfa295ad2944101831bf40489e6c22b5a33fdb3faa5347e2d18c25c227","tests/fixtures/invalid/datetime/second-over.stderr":"ba720829aee7506625c354e5f53dbd6b67ec351061df1e32b49cb11ea69bf52e","tests/fixtures/invalid/datetime/time-no-leads.stderr":"5117ac62dd754a203f2fd6e5163a2ce352776bee513219252cec77c0ed71bbbb","tests/fixtures/invalid/datetime/y10k.stderr":"ac3db71c252bb0b2f0bdc8a9d734ca60289afecfb5a66309a895b54c7d8e61f7","tests/fixtures/invalid/encoding/bad-codepoint.stderr":"64244879fa48b9c4f2f7b219325d4c308772c032b7005edd2ee4d771b5448bc1","tests/fixtures/invalid/encoding/bad-utf8-at-end.stderr":"d55ab89a2f111bbfffd362928dbe3a1dafe7f8f37a04e1c56307efe4f4ea14d8","tests/fixtures/invalid/encoding/bad-utf8-in-comment.stderr":"52e5ea4555b208f3ce91debc99e01dacc6e706381fb274259730b0a82f041660","tests/fixtures/invalid/encoding/bad-utf8-in-multiline-literal.stderr":"e22a4a60afcbf41351abb9d4d61a4c5d6dce246f98fedbccd33557eea8596524","tests/fixtures/invalid/encoding/bad-utf8-in-multiline.stderr":"e22a4a60afcbf41351abb9d4d61a4c5d6dce246f98fedbccd33557eea8596524","tests/fixtures/invalid/encoding/bad-utf8-in-string-literal.stderr":"eea9032a7d46e496ff8f6d4a95344b4d7660adb0fc715f652aefea54ce72d85d","tests/fixtures/invalid/encoding/bad-utf8-in-string.stderr":"eea9032a7d46e496ff8f6d4a95344b4d7660adb0fc715f652aefea54ce72d85d","tests/fixtures/invalid/encoding/bom-not-at-start-1.stderr":"dc9eb6707b252b77ee0c30bc471a4bd58308b31bfc892e2fd5c3995458e41b97","tests/fixtures/invalid/encoding/bom-not-at-start-2.stderr":"6570c6feea522cf017c8dcdc1e4b76cfeef2092bc3b938af0140e171817a8311","tests/fixtures/invalid/encoding/utf16-bom.stderr":"d9c278ec8c5a422f4553180ea76dd223278acc0d0ecc5f725235db6b379e5b99","tests/fixtures/invalid/encoding/utf16-comment.stderr":"2f2c4ffa18d08721d707b7083706efe5394e9fdd3504791a52949d76b7442e67","tests/fixtures/invalid/encoding/utf16-key.stderr":"8a8465ae12c6f3065edc3b237b7066bc8cf03b95a22403c8c5a1b1a5f085dbad","tests/fixtures/invalid/float/double-point-1.stderr":"817b02c19d53f02107569e7ddf9b38334cd85edd0a452795baaf6d5c0ffbbf63","tests/fixtures/invalid/float/double-point-2.stderr":"8b5a07c41087342d5f8bfaa8998465dae954cad11d93f01f4ed4a5541694e7d4","tests/fixtures/invalid/float/exp-double-e-1.stderr":"657c856a8d474cec0d557f4a5401456966bc82c8061a092977f4ae0cb71fb04f","tests/fixtures/invalid/float/exp-double-e-2.stderr":"ad101779127abb35a6e35020a4818c1622ae5433208bad57f1daf113357f6ab9","tests/fixtures/invalid/float/exp-double-us.stderr":"e6e53e58b5deefc9e50e6eca91423881b682bcf8528bfa0386158aad9458ae74","tests/fixtures/invalid/float/exp-leading-us.stderr":"5e45eeabdcf142ebd5b5ee2b77e2520c5ae7e35e943034e12c84095f51a1141e","tests/fixtures/invalid/float/exp-point-1.stderr":"2d4202fc806e23ce05f8bb3e63e31d1b4874875b018f28bd2b59c8714aa70cd2","tests/fixtures/invalid/float/exp-point-2.stderr":"d85145654b358cb63f44ea754c88f29a073fe5f637d07b279a986ddc0c347df4","tests/fixtures/invalid/float/exp-point-3.stderr":"3fb7d6151b522057a2b15137daa916681220cf575dec560f084d3c9c2dae1f3c","tests/fixtures/invalid/float/exp-trailing-us-1.stderr":"71aef4dde4dc2371702025127b9802540765a0c0db6f96156bf6542c574b30b7","tests/fixtures/invalid/float/exp-trailing-us-2.stderr":"c63f97484c7c498b01fef93984beadb9311d884ce5b7856ab1301cc398ae9c4c","tests/fixtures/invalid/float/exp-trailing-us.stderr":"f5bbc5fdf2dd018c031ca5c79edd7f16a0da25676bed96ffc0682401497ef362","tests/fixtures/invalid/float/float.stderr":"511c7e989b6e4768f089634036848acc896439a06df83a97fbcb92ade1554e98","tests/fixtures/invalid/float/inf-capital.stderr":"d493ce9b11dae0dea40a2c8c7666125031d5323ab82bf1b8af997049d60072fa","tests/fixtures/invalid/float/inf-incomplete-1.stderr":"6090e21b98dc6df8e67962c8458ac773bf3dd4b78ae4f4453dfbe01657401a7c","tests/fixtures/invalid/float/inf-incomplete-2.stderr":"abc3855709f099a9023f0d3e305edb8c97570223f889431dad32e21e83747d6c","tests/fixtures/invalid/float/inf-incomplete-3.stderr":"9fb99f8ca91b9fecb86ce27e1b4fa6ca05a2d5c0ab039972d4d2a7dcf2c6228f","tests/fixtures/invalid/float/inf_underscore.stderr":"8e7202c508c9d6e35410e4ff89bee3a5db4a88367e69e210fd4ae4f9bf602286","tests/fixtures/invalid/float/leading-point-neg.stderr":"21e3e8ea4c817ca286b1ee6dd359c91b6e8ffcb376bed393e03a29e55d7a6a61","tests/fixtures/invalid/float/leading-point-plus.stderr":"ff0f7a8a5cdccfc7e6d3e6b1381643b759f5aeff5546bfd2fd4dd29f8f1b1551","tests/fixtures/invalid/float/leading-point.stderr":"5c7590482ac55bb0d11eb9e1291a80d8c891ed1949adfc64e81b75bc5f8c8b06","tests/fixtures/invalid/float/leading-us.stderr":"67ce463deceabfa0dd4eeebfa68d72eb53fd235455d6181b4c8597c6507e0ee7","tests/fixtures/invalid/float/leading-zero-neg.stderr":"16b6cf97171ec9f85a36e09a1ef39087eff3a0c441a9f9d96c43a9d0fdb5ee38","tests/fixtures/invalid/float/leading-zero-plus.stderr":"f08e1416644c743b3463342313f023982051e992756e9784229b1bcfc1e19aaf","tests/fixtures/invalid/float/leading-zero.stderr":"511c7e989b6e4768f089634036848acc896439a06df83a97fbcb92ade1554e98","tests/fixtures/invalid/float/nan-capital.stderr":"66fe63db08d9ee24e8a90f4e2b5f2c15fb3f35dfb7eb81ffe760f8957967370b","tests/fixtures/invalid/float/nan-incomplete-1.stderr":"138d0d28aa5e90b9fb344c743478c0b6be79492ecac856d71e239af5f6e1aa99","tests/fixtures/invalid/float/nan-incomplete-2.stderr":"31f200fbfc6e4c8d06f58960dd7349f650af86d22a1f0d6fd8c4df46bb4a0b3a","tests/fixtures/invalid/float/nan-incomplete-3.stderr":"3b16f8d82cdf18df21bba1e83d2aa33e175ab5a37fd5f3214b1be84a07acab10","tests/fixtures/invalid/float/nan_underscore.stderr":"4a30507eaf227e8c8e2bbda6d7b3cb4fdcf793216981d1f9302cb2b049e95d58","tests/fixtures/invalid/float/trailing-point-min.stderr":"0b5c7385ced699074d6042b5fa0ea9266ea64a191dd0e43466ca71a2ceaba76b","tests/fixtures/invalid/float/trailing-point-plus.stderr":"856edc036309660c223b1cac7c99d9d6b61bb4b49e592410c3f308fb0a678800","tests/fixtures/invalid/float/trailing-point.stderr":"5241f4525c36c1cde19d520aacdfb7db1bdea07710d2621d2a60ac7b1d1e70a9","tests/fixtures/invalid/float/trailing-us-exp-1.stderr":"42a7b0d43ef2b548c2199a4c9ade5b30c28b91a625d6bac6f57d721b4fff2848","tests/fixtures/invalid/float/trailing-us-exp-2.stderr":"13c1374bbf3f5e3c762b6246dc8ea1f5e619b3edd6d66a2e37ba51fe3ba0acca","tests/fixtures/invalid/float/trailing-us.stderr":"4103e29434d2c323c7461903cbe27dc0a5a70c9907d2d47ee31eaba3f4d51b2e","tests/fixtures/invalid/float/us-after-point.stderr":"2d0ef75d124b8ff10a48639d2f837a13cb18ae66891c9fc3709199ff724c1af3","tests/fixtures/invalid/float/us-before-point.stderr":"844c0d11f60522784f6f22f7f3d418c7e6f16cd96cafa55f542996974fd267c0","tests/fixtures/invalid/inline-table/bad-key-syntax.stderr":"9fe839d2c861b2026ffcbb37a7aaca3498df90f66da6d402d5ab96ff357de453","tests/fixtures/invalid/inline-table/double-comma.stderr":"67e92c9f776a5bd04f908a79c6f2e8c146ebb6072129ab7fbd9606ed8d56fc44","tests/fixtures/invalid/inline-table/duplicate-key-1.stderr":"3eded78c2d76205b8f516e180f8c8849d6ba34f04df72c5ffac319264d16f40f","tests/fixtures/invalid/inline-table/duplicate-key-2.stderr":"a166a188a3682704cf70153666734fe63ccc23075265d0d20ebfa25ba7a633fc","tests/fixtures/invalid/inline-table/duplicate-key-3.stderr":"3e4cad3fdeda14976a0e6193465ac7b0c052a075b55a4dabb95ec2a03d7ef532","tests/fixtures/invalid/inline-table/duplicate-key-4.stderr":"2ffe3212ff7d4dbdd7f41f0c0d037599d223c1876af8c9fa25770431abd7c6f7","tests/fixtures/invalid/inline-table/empty-1.stderr":"ee1624b0ebfc28edb9af042a4926d6b5b62fe7b6265544b158b577bfab1f0866","tests/fixtures/invalid/inline-table/empty-2.stderr":"70c376a4f2368d3586184e70110115525f35cfef2fd15bd2c0a9579a88868cd2","tests/fixtures/invalid/inline-table/empty-3.stderr":"fb05dcafd2b5e290ea8de8d0b8d9ad0fc58452d75c537e8482a2fa7fb714126c","tests/fixtures/invalid/inline-table/linebreak-1.stderr":"016daca1cf56861089265156af1ef41772ed0c5fad6848cbf8c8c91ddf748ade","tests/fixtures/invalid/inline-table/linebreak-2.stderr":"b259282b6bc1a9f9c96de41d2c5b0f27b9d8648ec8984aeb6760cc15ff5714d3","tests/fixtures/invalid/inline-table/linebreak-3.stderr":"59b34b9c76c57e02df83c8ce97b1f3e856026cbb33416d7335f287222e11ec35","tests/fixtures/invalid/inline-table/linebreak-4.stderr":"8a78e7240345fc8b850cd54d3f1c74187e4abcbc65b85e3b1da79ebf3e139d70","tests/fixtures/invalid/inline-table/no-close-1.stderr":"6da0e9a2d2d272d199745c704aea6bf317921d8ab005882a3de793e6ff87e863","tests/fixtures/invalid/inline-table/no-close-2.stderr":"eef11b2fc399ca9b7a397c5366a9b7a9021e783cb189d68fd5c29cc607a5f4ab","tests/fixtures/invalid/inline-table/no-comma-1.stderr":"d2a33a967d517c9690b90845347d26d8845c997ba4ed61c640a58cdf919d7c7c","tests/fixtures/invalid/inline-table/no-comma-2.stderr":"c4452ed96f19979b36019c568fd8c73e72db2711a87d17cc8f62362b88598b30","tests/fixtures/invalid/inline-table/overwrite-01.stderr":"4e47f343291068cb0164fa2d48be2c3257458ee7467cd60d664bfbb83713f2f9","tests/fixtures/invalid/inline-table/overwrite-02.stderr":"40718dec554fde10db86ab240f5071b35ba564503962938f7b9464341e7b27f1","tests/fixtures/invalid/inline-table/overwrite-03.stderr":"0318c1d4713cea361a857d382bc8b150dbb1ba8f8e613df472e936bab38485a9","tests/fixtures/invalid/inline-table/overwrite-04.stderr":"9e55cced7664c821c073937321880bf75150affa44d4363c774f5834bbac7470","tests/fixtures/invalid/inline-table/overwrite-05.stderr":"59a0ef41c6059994212244c16a8808b4ac721c710f1364639f7116d43a7a8b26","tests/fixtures/invalid/inline-table/overwrite-06.stderr":"c2a6060831445d4143254c6908468bb04cb5be501161028429467a2dbb68e235","tests/fixtures/invalid/inline-table/overwrite-07.stderr":"cbe9f4d1b428e658591e077790986a6589385474efd2ae9f488b6003715198ca","tests/fixtures/invalid/inline-table/overwrite-08.stderr":"d85341eff5c078131c65c801a259d0909921c541ea5a8eb75e752e5d174092f4","tests/fixtures/invalid/inline-table/overwrite-09.stderr":"99c73b045436b330b4f6570bc1a40726dec71a524e44f6b63a8c55ceec4e5f4a","tests/fixtures/invalid/inline-table/overwrite-10.stderr":"746d1cfde95d2c5fd748e4b95fb95383e014a00f73c03625f7ebefa52243e815","tests/fixtures/invalid/inline-table/trailing-comma.stderr":"079ea824e27cae66b32160b99d6fd75b38514960c440ef14274528fabacc98d3","tests/fixtures/invalid/integer/capital-bin.stderr":"9224535b369db24906be0bbbac4ffa80d1b570db8135ce7f58202956cf889857","tests/fixtures/invalid/integer/capital-hex.stderr":"ee731f89283c425e7c54cabd439651cc4926c1d721ee92ec809b123521ff7c91","tests/fixtures/invalid/integer/capital-oct.stderr":"52005625e8e51afa09a5163c9eaa010e09830727128d68a1e48fa600be6823e5","tests/fixtures/invalid/integer/double-sign-nex.stderr":"819ab532a90faab548580eb0a959199466acfcd04d29d77cd0af7acba509cb1c","tests/fixtures/invalid/integer/double-sign-plus.stderr":"94f6b7c5dbe458ebb1504b7672a36c9d115e17b0e629dcfe47360803c9cfa923","tests/fixtures/invalid/integer/double-us.stderr":"c9a377fc0417b12123e9cdc4b2fe69aade63e532e20e65f279d7b4460ad05e49","tests/fixtures/invalid/integer/incomplete-bin.stderr":"143b9a89feb99b5a420bdc6aeff919ebf6d53bc011f31afbee5b8677a8ac84bd","tests/fixtures/invalid/integer/incomplete-hex.stderr":"b246cd8117362f5451c60298a318d6f93233bf9959c73cdd7a6d590d226291c2","tests/fixtures/invalid/integer/incomplete-oct.stderr":"4cdbcf8ce0a743a9442321a15c01670109debeed6a9712a95d2cb95a9debc469","tests/fixtures/invalid/integer/integer.stderr":"1afca937abf7f267f8c8e9d2112309d1aa2dc90ee1e2d4328f5a30b19ab70941","tests/fixtures/invalid/integer/invalid-bin.stderr":"e94521b61ae2bee5032073007ddd027945723f8c7faf3b4523ba1f8e9604160e","tests/fixtures/invalid/integer/invalid-hex-1.stderr":"5873555438d114b5d1eb323cdf95a651bef7f95b4c1259d657fdeb993350a2be","tests/fixtures/invalid/integer/invalid-hex-2.stderr":"fac8c488203e501d3ff383313f8a1beebfc5ff5676eb6624ca9a774094105bdb","tests/fixtures/invalid/integer/invalid-hex.stderr":"521096bb5d0c10e420a452adb133eecee4e153809a3c29822e64569e4ecf2779","tests/fixtures/invalid/integer/invalid-oct.stderr":"cf940d8abf0a98dbf4b139089cb964964b30e6fbdf89d9a11bfe94b06bbb3547","tests/fixtures/invalid/integer/leading-us-bin.stderr":"34a6f856274435b07ed74ddcbfd0c471920b418858fc2685eacacf8b814d2222","tests/fixtures/invalid/integer/leading-us-hex.stderr":"a468c4d3ef104f5f575ef61e01a2889bd52112fe00917380d9fe094e7f1f0625","tests/fixtures/invalid/integer/leading-us-oct.stderr":"c6600b9cac7b6a935923b951b24051f2494fdc2d351c0f109f63da546a280315","tests/fixtures/invalid/integer/leading-us.stderr":"4c156a62e39d8404bcaea001ad347aa3366ad956899fb385492203d17da9b34e","tests/fixtures/invalid/integer/leading-zero-1.stderr":"1afca937abf7f267f8c8e9d2112309d1aa2dc90ee1e2d4328f5a30b19ab70941","tests/fixtures/invalid/integer/leading-zero-2.stderr":"4e4ce0d6714fb455def4a586c076072bc8d83b87f3b62122611b6e62df5fe532","tests/fixtures/invalid/integer/leading-zero-3.stderr":"d28a4f3b59454eb4a5756d9878b54c03a154aa3af32a9bc4b746fa8d4c842454","tests/fixtures/invalid/integer/leading-zero-sign-1.stderr":"b84ffda89f4797a517f8488b59ebd795fa42e59be7719b3883b2290c8f1060db","tests/fixtures/invalid/integer/leading-zero-sign-2.stderr":"3484e932794a500ed92322323032f7ca2c95fda1af3586fe3d9d5d0cdcdc41e3","tests/fixtures/invalid/integer/leading-zero-sign-3.stderr":"1f7afe7069d13990918c68a32ba46a9e17a305ced699c94cba72b01eec5aed93","tests/fixtures/invalid/integer/negative-bin.stderr":"b3825a06a49dc04be695629fb45ad33a6882ea73821c3c42db7dc9ebc878b5e8","tests/fixtures/invalid/integer/negative-hex.stderr":"f54b8ecd33db30a04ff9312f4b7a42d06ce8c276062a56bdf3773b10e267036c","tests/fixtures/invalid/integer/negative-oct.stderr":"12a30a960af847a2762336b6b0569d603c84dcb31a694bc93a136fa503c298e6","tests/fixtures/invalid/integer/positive-bin.stderr":"f4f91449d9863cfb107f4f174e12e64b01e676c36bab77a431078c9a0fda6e94","tests/fixtures/invalid/integer/positive-hex.stderr":"1ea707ef22ca30bd0ce9becdd4c6c3f04a65a79c153ec38875b4a8626c62dbf2","tests/fixtures/invalid/integer/positive-oct.stderr":"f033b6c19e3f1f04c69baa8ebc4156ac913be0428e85fd474a99d57298c23e75","tests/fixtures/invalid/integer/text-after-integer.stderr":"581885f158d84ac387b8bfb483d4bb0593e23bbf251a63a542ec81630ffd0bd5","tests/fixtures/invalid/integer/trailing-us-bin.stderr":"7e5928cd15695fa35403a024856f64cbbf7a056e8079f76d1923e38401086e41","tests/fixtures/invalid/integer/trailing-us-hex.stderr":"83d3672baed59c1b0ca8b27400b469cd18d2c0790ececd9d5dd72e283ceb4584","tests/fixtures/invalid/integer/trailing-us-oct.stderr":"e048d0b7900704573a076501b5fa48289dbaec4ebb279f10775ab0b8c5060dd6","tests/fixtures/invalid/integer/trailing-us.stderr":"9e46f25d350e02fa57100d87917d9e6a961907df89955c01bbd2bb473f807297","tests/fixtures/invalid/integer/us-after-bin.stderr":"e833ae217e60edfe08f5c76fbbd8ec2bf6de9b41b16905e474791f1b5bc4695f","tests/fixtures/invalid/integer/us-after-hex.stderr":"d5cd357750289db24ddba5d8278eef6073f591a05bb495096e8f6214626187e7","tests/fixtures/invalid/integer/us-after-oct.stderr":"7254481b53b8602413ede570876963fb6ea9c2699adb986ee17023cabf5916fb","tests/fixtures/invalid/key/after-array.stderr":"8489fa15adf8e674687aef4a8ef8acc10235ada0e7a3b2fa17aaeae9f9e02189","tests/fixtures/invalid/key/after-table.stderr":"d1856cc75dc16c95c045ae4c05982448e3c2680057cc1e08c09ebe68ffbb35c8","tests/fixtures/invalid/key/after-value.stderr":"00398c3f1305f387b9ff9f17dcc76061350a800e652e00ff2f3892993c42ea33","tests/fixtures/invalid/key/bare-invalid-character.stderr":"19220d575145a93b522e39f59ee63fe0418194fa2b80d9649916d4b8df556147","tests/fixtures/invalid/key/dotted-redefine-table-1.stderr":"169e40f3d1be87cf099a9e7c6e39175ec0fd701c933b427cefc300d2e854aa07","tests/fixtures/invalid/key/dotted-redefine-table-2.stderr":"72d632c3572aa914603d154152e10157d4db3d90db0da0eca88baf1416b3e29f","tests/fixtures/invalid/key/duplicate-keys-1.stderr":"f4207dea6f110318da0c9c71b3957c8182b133bda91911804a98bac71c025cc2","tests/fixtures/invalid/key/duplicate-keys-2.stderr":"a5e1fb5b90daf5b7e659ed76c82dd0ff6cf2a09e3578c98bdf907bc7801614ce","tests/fixtures/invalid/key/duplicate-keys-3.stderr":"c8d91fe50b91ad091430b9699aea28a42b7822373c43fa064900721df066d45c","tests/fixtures/invalid/key/duplicate-keys-4.stderr":"ad171657bb84292443753bdf9f2963cb268a45dee78ca4f39dd474b6698fb020","tests/fixtures/invalid/key/empty.stderr":"0da32834de6e29b7254260b51b5b26f1537111088f2dfcc7085cf717aa9c7b42","tests/fixtures/invalid/key/end-in-escape.stderr":"f8710ef2b803f053d1c2f85204f7d9beadc485c4a5525d1c17d8c8cc228c0eca","tests/fixtures/invalid/key/escape.stderr":"11c5f871342cd20a90270eb3cc47c06fb186e7493d27026f5936e78c0bd088b5","tests/fixtures/invalid/key/hash.stderr":"55122578bd7c090a641a0aaf980aebe3fd2c1fcf820da4d72408bfce527a5293","tests/fixtures/invalid/key/newline-1.stderr":"32c0795720a45125ff56a3e0a20ce62c2a2174fc02f44f2dd6620db15bc56547","tests/fixtures/invalid/key/newline-2.stderr":"f2d74552b3489eb7f89ec8f4cfb5084ac21ad338ef2a0d320a57c0eca30fe5bb","tests/fixtures/invalid/key/newline-3.stderr":"672d411c92fcfdb495b41a15e6cebf0e73d4484e6a0902772b9937f5ebf35c61","tests/fixtures/invalid/key/newline-4.stderr":"aa2b599e10e8a20bec1d1fe112ec90ccb5bed8d42e43dba8ccf0d5c18d4b131c","tests/fixtures/invalid/key/newline-5.stderr":"961866b075ab56f652c9661e900070b729ce5db2f57210e95f9c3955f5ba4b41","tests/fixtures/invalid/key/no-eol.stderr":"e8f14df57190fc8a0f4e704a38c42126aef0663f9315f84d23c371f0860a5a73","tests/fixtures/invalid/key/open-bracket.stderr":"330a1a96948b442437bd81f461e91e0b328e225abd304832b7d1660eebc77c5b","tests/fixtures/invalid/key/partial-quoted.stderr":"29dd53ef16e939b137da2c7d62c24aa2481bd44359a02578b77597296b25efc8","tests/fixtures/invalid/key/quoted-unclosed-1.stderr":"6b566ea67dd061ec05c6754bebdb583d93934412cdc6176bbd68d3bd403cca7a","tests/fixtures/invalid/key/quoted-unclosed-2.stderr":"396c4f9bd1ff398b439ae54b23452007e1e7518b38232b89b0f5b1b448e764cb","tests/fixtures/invalid/key/single-open-bracket.stderr":"52d8e13b39b9507ef37a65c0eb779e89ff7d00fd4a70245d44225a35200a6ae7","tests/fixtures/invalid/key/space.stderr":"9f3d6ed39f1a05ac99bedceb66c53835090182b47293bdfa0c080ac80285bbb0","tests/fixtures/invalid/key/special-character.stderr":"5fa9a11b46c2ccf749e3e4702c2000ec6b95abdb864eca0653943c669fb58040","tests/fixtures/invalid/key/start-bracket.stderr":"93a0e8ada8447d60ae738b4c0816d9087a84932dad2651505327ff37d3b12c86","tests/fixtures/invalid/key/start-dot.stderr":"25b23ad9a454746edcffc91b79eee11bdc9d9daf67640aa8d28594a98da3ddc1","tests/fixtures/invalid/key/two-equals-1.stderr":"f5398f176b3b1a3fac4cc7d68d1e595e2f1b60f92ebdd4f3f9bbe5d7191eef3e","tests/fixtures/invalid/key/two-equals-2.stderr":"099b5d50827299b921c3c1c00a2c804d580f67d06c2e5593660c6d6ad5900a66","tests/fixtures/invalid/key/two-equals-3.stderr":"b51c719bd0d8f585b4b017937e7d0df2ded58e3a37e2cc17cf9bf04277347a1b","tests/fixtures/invalid/key/without-value-1.stderr":"b1a087f97400f49a9bd6ba8bf44b3afbc3a0db6423d17b95fca5fc5be7a3a775","tests/fixtures/invalid/key/without-value-2.stderr":"cef84c2625e41ab3fda621f35d5e936a8d43bd292f3856f80cd2a148efc6d6b7","tests/fixtures/invalid/key/without-value-3.stderr":"295097c33c87dd153e607b3aaf648168678e6773e28ed0148d0b2e5144c1c0e7","tests/fixtures/invalid/key/without-value-4.stderr":"65173e2596ae1ac268c1f5a66fcb2e83be5663425aecf3fb3a99091b6fd40ea3","tests/fixtures/invalid/key/without-value-5.stderr":"9279c3cddcea9568abe57303cb5de41d8d1c71ac4ec991e1fd28651776314b57","tests/fixtures/invalid/key/without-value-6.stderr":"dfd20b35750e3a3cf1da7e8e1f20043235c0349cfd118bb12cf1a42165cc7ec5","tests/fixtures/invalid/key/without-value-7.stderr":"d0183883abf80ce86271bb0b8704637f93830cccb4c853f7834d132c9f153d4e","tests/fixtures/invalid/local-date/feb-29.stderr":"4c97e157ddaf9940d438560f34642ec4903609417905c02456ae386ade5573bc","tests/fixtures/invalid/local-date/feb-30.stderr":"1710668c24dd3aa9fde6e0f782eb880af872e4bb5a5186b281c8e6ed40e86dca","tests/fixtures/invalid/local-date/mday-over.stderr":"546af4611d7a589f4907bb085b74a5d74aab7505b613c448d8075aa9f14633d5","tests/fixtures/invalid/local-date/mday-under.stderr":"102c21e5b546a4a069c576d7ccc5a1bc259522e951fd7487e7ceb26f982bcc43","tests/fixtures/invalid/local-date/month-over.stderr":"4cac731d0f95392744498706c91b4940a30254d3910856da666bcdf1610c80fe","tests/fixtures/invalid/local-date/month-under.stderr":"df4ca9f526a23709fe9feb51b71b5ae3efac806a3fb337a40b626a27104b4124","tests/fixtures/invalid/local-date/no-leads-with-milli.stderr":"67e6de0c6025e5f498e0d5e0c7bdd2cd74a01cefe0a3992ad935b7b7f260fe93","tests/fixtures/invalid/local-date/no-leads.stderr":"d653403d2686bf1e56c21a120b7a6fdfd6e0f5bd231b14789259c23028e16dec","tests/fixtures/invalid/local-date/trailing-t.stderr":"9cf2e418427abe378484486356229b38f1db53842090a2d7d0637cad1a6b16c5","tests/fixtures/invalid/local-date/y10k.stderr":"ff9286e64bf70a2e35bed170d191349f0045fc8d6f184a183cbea693496e99e8","tests/fixtures/invalid/local-datetime/feb-29.stderr":"3fbdaad131492c5e7c85df1e90f73977a2f1236422b5fe86847fa000dbee7e5b","tests/fixtures/invalid/local-datetime/feb-30.stderr":"32e38bb349f6cde4e60534764f5839240313caeee12ddf4d97d4df404a6f3b01","tests/fixtures/invalid/local-datetime/hour-over.stderr":"0aefa752706aca82810109a94b9a5bbb8f50ebc4aa85bfb091466618c714c1f3","tests/fixtures/invalid/local-datetime/mday-over.stderr":"5873d8c67de9fd55e424aadcdb68d42842ccdee9f2e2e94e0bc42caea6635645","tests/fixtures/invalid/local-datetime/mday-under.stderr":"a1df2d77cf02f3cfe2a44daa946978aeff84db9ee033e61358e2610ef8acd3d3","tests/fixtures/invalid/local-datetime/minute-over.stderr":"1717bf680a89f59a66d5dae8dc98a17757b8973f70f869744ce6b496fa2c7c9d","tests/fixtures/invalid/local-datetime/month-over.stderr":"ffc05504137e79700f4c78955470e975bee840580cf10229202903a498e09858","tests/fixtures/invalid/local-datetime/month-under.stderr":"8e77bd4dad24e1d7e5c017702410cea56a085585e7d97425c55761b0246efde8","tests/fixtures/invalid/local-datetime/no-leads-with-milli.stderr":"88a35c260a65588d5ca0246c0f27f5e5e6fe8836777e5998c9f3c4bacca1c8f2","tests/fixtures/invalid/local-datetime/no-leads.stderr":"40795bab32c16aa63e609add7f809c7c22d2764d98700b3eb91c20e6eea156e8","tests/fixtures/invalid/local-datetime/no-secs.stderr":"f946633b31380b72df35d6a0e371f4eebca26332abac1c3d2841918461df30cd","tests/fixtures/invalid/local-datetime/no-t.stderr":"802e0bcf8a66b32fe9f2d0571808782bd37a6f1ff8c5e92368b5ad4c18d2218a","tests/fixtures/invalid/local-datetime/second-over.stderr":"7494350b21ac24f7f30c95f568412d7214a66dd9b04fbf454647f0972588a7fc","tests/fixtures/invalid/local-datetime/time-no-leads.stderr":"5117ac62dd754a203f2fd6e5163a2ce352776bee513219252cec77c0ed71bbbb","tests/fixtures/invalid/local-datetime/y10k.stderr":"66cdcfa2f4dbd5270936e531a1f3a9ac54a70dec246f3ef93b180911c201db1d","tests/fixtures/invalid/local-time/hour-over.stderr":"0f0507c0f3888330f1346e6a4c100c4bcbbc6549599cd39249fb59c7923b1801","tests/fixtures/invalid/local-time/minute-over.stderr":"052a762b6f62e21d59d145da3d242489990f99023681a4a645022af743a836be","tests/fixtures/invalid/local-time/no-secs.stderr":"db11e76aebb5c91f57869f1dd14ff7b0ab19b400eeb8b15936d0d82e4a8d0fe0","tests/fixtures/invalid/local-time/second-over.stderr":"18402b0e6cf205f466e3ec3d409daeeb77e355d41548f11bd3e3369240a282d8","tests/fixtures/invalid/local-time/time-no-leads-2.stderr":"2e446fcabdeeba0c118d1d49b89d7ad1271dd9eaf1ba109ee445c4f68ee96214","tests/fixtures/invalid/local-time/time-no-leads.stderr":"70094ae81f89871b0804839a5cea0f7591ab55b6c72c5d2867004cd4a0e0d86f","tests/fixtures/invalid/spec/inline-table-2-0.stderr":"41118ecf5033fdb7de4edaf1abf90e524bb50f8f3c44ef0a080be4ba1470b82d","tests/fixtures/invalid/spec/inline-table-3-0.stderr":"b7ea14eb8c6ba2cb126dc3947d00ce10a5403103a682e719ff51efebae6bb613","tests/fixtures/invalid/spec/key-value-pair-1.stderr":"d8b4149dc0ede93fe7c0d7d02a0df9d31fb2456b87ebeb532623bd6f825c2d23","tests/fixtures/invalid/spec/keys-2.stderr":"de2e84dc37ec53bc12ca585a9d2586404a920375bc201d7b2643fdcd537da691","tests/fixtures/invalid/spec/string-4-0.stderr":"c6dc77f81979a92cb5612b7f4b8e96d41bad9bf7a9a706bff5a05a6c65f76e86","tests/fixtures/invalid/spec/string-7-0.stderr":"3226561489c03d8fc983b0584d40f18629f46a425c5c7f34a4e984e164a4eb38","tests/fixtures/invalid/spec/table-9-0.stderr":"23b1d3b522e9fac974c4ea254df4798adf574be6dfc8ef1107438c502b3aca4e","tests/fixtures/invalid/spec/table-9-1.stderr":"f0164ed288eedfed3a6fecabf305a93050a6c3cc6a9397894633e4ac33b2c834","tests/fixtures/invalid/string/bad-byte-escape.stderr":"5807e3215b0def7410283291aeeb46514fa0a65d4326bff3669585e0222d5b2c","tests/fixtures/invalid/string/bad-concat.stderr":"176458d0d31d02a16e9856a80b1b9c4dc040b3ee3c83c8a678df9b1bd12c1e4b","tests/fixtures/invalid/string/bad-escape-1.stderr":"388e58e25fd5fc78b2465f1258c952f4850211b19e9fb65cc31a29774a19aabd","tests/fixtures/invalid/string/bad-escape-2.stderr":"51829779b7317fe753c54b53eb08fba669b16fc970fc29699a0a497aa95dc623","tests/fixtures/invalid/string/bad-escape-3.stderr":"8e5050c043061a991f6cf84be6b6e6ce468a35dd2ac50a3a4ce2ca1e1d5304e1","tests/fixtures/invalid/string/bad-hex-esc-1.stderr":"68446cc8fe418df4441e15118161a953cbeb22822d67c54b81c8f97aa48005f3","tests/fixtures/invalid/string/bad-hex-esc-2.stderr":"d7d979d0c5700548eae7a42c7e87253de0118b67a67ef6dbeb0598d574800099","tests/fixtures/invalid/string/bad-hex-esc-3.stderr":"fee92d2f9937e322423201b3099c7a246fabae85ccc23e990b602e1f5e181d9f","tests/fixtures/invalid/string/bad-hex-esc-4.stderr":"2c6f994aebcd9e644f469c84edb9d8ba6b6b094a949410935a590e8e6e6122ef","tests/fixtures/invalid/string/bad-hex-esc-5.stderr":"10f740ce3c36f4a8f7a1f8ab5c84e0fa5e4c3a88ebe534f79858781079139a96","tests/fixtures/invalid/string/bad-multiline.stderr":"a1ecdec206e87461bb4bb27ecd21a43da48d1d5bef6d8d746958d45219ce9dfd","tests/fixtures/invalid/string/bad-slash-escape.stderr":"7f3b917db5a8d87484f8b03680a2cd60288e549c5bca0c9e00c7e531d3e0405c","tests/fixtures/invalid/string/bad-uni-esc-1.stderr":"385be83541cc94367c9849619740c8d198b9c138f6f4916685cead90fabc3b9b","tests/fixtures/invalid/string/bad-uni-esc-2.stderr":"592fd536f48c1aa6c4c9b454c5cf5e1a7473860602ff2875430d4a7e676b6594","tests/fixtures/invalid/string/bad-uni-esc-3.stderr":"f6c284685af63ac02fee8e8ee641b03e90893a03001834625ca22482cae4d29a","tests/fixtures/invalid/string/bad-uni-esc-4.stderr":"1df7029bc305307943cc58417f98cca4c854deba8475b305675b2505d4d9b83f","tests/fixtures/invalid/string/bad-uni-esc-5.stderr":"6cfc8fee06b9770271b1241ba6025a470b1ca8cbb80a3dce7d6ee1b2c2f7f637","tests/fixtures/invalid/string/bad-uni-esc-6.stderr":"73d8f5132741c15286327111496d475807dcb638b34c10db20bb37d96910c209","tests/fixtures/invalid/string/bad-uni-esc-7.stderr":"c9c967322b827675040ad78cf4fa1c9890bb13a124a2274eb693622d5ec50420","tests/fixtures/invalid/string/basic-byte-escapes.stderr":"50d06255616038364b52a0a32113d96e8b87de8e5ffcef93c2c88d0af9b57670","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-1.stderr":"b26e80921025c1fb0dfe33c01460e5ac96791585ab8204db3e76c83ccd404afb","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-2.stderr":"c9f8c422be49fb11111623c4322c421b25bb9592fade95bc1cc2ed9c3c2bd93d","tests/fixtures/invalid/string/basic-multiline-quotes.stderr":"70de515fa0500fe6ba3b915d224653824ae4e213cf41d9b3465b536a0007c623","tests/fixtures/invalid/string/basic-multiline-unknown-escape.stderr":"2c104ecdfe88a89893d167ad41167108208d3aeb0c22c7d5193950ac2805af60","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-1.stderr":"82b7d85f48c58289c81d149814c591195f361e23875dc2a9767215bf077a148c","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-2.stderr":"9b28ddf18556e9adaf77319877189112aa8ac93464ecc09a8c5020c3c13b143a","tests/fixtures/invalid/string/basic-unknown-escape.stderr":"81ebb5efff40faa0b5161e4d92faac81aaf85c038987ca3d0ce7ff28f91f3cd5","tests/fixtures/invalid/string/literal-multiline-quotes-1.stderr":"6776f505f0a2a6a67e3e5fbfa6ca0b9ca2653a1a623d1fb3b463305ec872699f","tests/fixtures/invalid/string/literal-multiline-quotes-2.stderr":"e304eb9ed3f7a136c74a1c70fe73f5f75a169ea93aba2789e6441f84fbcfb03c","tests/fixtures/invalid/string/missing-quotes.stderr":"ae9dea4e735224472bfcdb61471def4877404a0a3680e41340b0495552ea8133","tests/fixtures/invalid/string/multiline-bad-escape-1.stderr":"7c5fc3bf00f17344781150281839f78228905d85e41895f4c6d5fb67c7ef0a2c","tests/fixtures/invalid/string/multiline-bad-escape-2.stderr":"c4f5464eced80eb93368ee62ec14c6c4c8160ec7e33000b5d7840f748306b2ea","tests/fixtures/invalid/string/multiline-bad-escape-3.stderr":"ad14d3d916a15353394638613d6d79da2e004e7e522c48edbd2ff45edbcb2685","tests/fixtures/invalid/string/multiline-bad-escape-4.stderr":"e0b254f4a49cde181f4d8d94e032299e1506a09e8bc5bd54590a04251d5978ab","tests/fixtures/invalid/string/multiline-escape-space-1.stderr":"addc40c648a77da6e8ee3d63e73e58331bc5f8cff6d4ba67bfe679db464de835","tests/fixtures/invalid/string/multiline-escape-space-2.stderr":"8e9cc938972808cdeb4e8d2f180e1ff856000eb52f3e77ed5149658c15296d75","tests/fixtures/invalid/string/multiline-lit-no-close-1.stderr":"5e57a18bec64d8ca2fd53473aff6f1cdc8e5c35209ecf84a9e9da8ef3865fcfb","tests/fixtures/invalid/string/multiline-lit-no-close-2.stderr":"40a6c9ece9e2b03c137283b825b6c0380e5778e1c76d20ed2f46647e3ea6b25d","tests/fixtures/invalid/string/multiline-lit-no-close-3.stderr":"cfefa000b457e225de66a0618b2d245c0887099cda67c6ced68612df58a50c49","tests/fixtures/invalid/string/multiline-lit-no-close-4.stderr":"12629cc0445ac282832bebf35e0bb2eb1e305a4a4c147f4103773501d56d3bae","tests/fixtures/invalid/string/multiline-no-close-1.stderr":"40f5d85746b8d297b1cc27bfcb95b8bd2a2a471f0b820bca0b3437c4ff6fecc2","tests/fixtures/invalid/string/multiline-no-close-2.stderr":"3c121acbcad1ef6821da5452f0e9223f75915be50ab11d69efa498cd2c9685a4","tests/fixtures/invalid/string/multiline-no-close-3.stderr":"9850d68e589fb8ebe7b1cbf9f27e4ac991d27b939a5be577df02aa9dae58207d","tests/fixtures/invalid/string/multiline-no-close-4.stderr":"dd0703ac4d584850805bb5a7afdb1e68db06fef1dbe2a93ba85b01ec76da6b9a","tests/fixtures/invalid/string/multiline-no-close-5.stderr":"c6874fe259394f8a14636182b4fb0f20009d678853fbf4f5c7e0e0d53fe0b789","tests/fixtures/invalid/string/multiline-quotes-1.stderr":"4d524fc6b34800ef6093894044763f1eba2b4fffc110b2558ad4f76249985651","tests/fixtures/invalid/string/no-close-1.stderr":"ee585c98fe640bd5a6edb8c5930358b1a7c9085d07e7fb318bc094e81dc6e1a5","tests/fixtures/invalid/string/no-close-2.stderr":"2284df23ebcab2f4a535e31726035f5cdda9d56190269a8189bd959fb806c636","tests/fixtures/invalid/string/no-close-3.stderr":"30cb3d9d517219b70c7d468207f0f4567d80b00330a943f482803980d8f65998","tests/fixtures/invalid/string/no-close-4.stderr":"4ff50513bc0249a3e503db5f255c1251bfdcdb764a6541d484f7e09bf4efbcab","tests/fixtures/invalid/string/string.stderr":"68446cc8fe418df4441e15118161a953cbeb22822d67c54b81c8f97aa48005f3","tests/fixtures/invalid/string/text-after-string.stderr":"a20980f6482cb204272c705c89ee6cc0987b44c5fb357466f05fe442754c1cc7","tests/fixtures/invalid/string/wrong-close.stderr":"6b376b179dd7022bea271e63e77f0afc15076cd4ad0c81bae6007fdc430072a4","tests/fixtures/invalid/table/append-to-array-with-dotted-keys.stderr":"76b544968f3a5771cd2f5efad26167ab5bdcbd8b067586ec0e602281ae5869dd","tests/fixtures/invalid/table/append-with-dotted-keys-1.stderr":"1379eea8bb3f62ddc212705a525cad262da23f16b03bd0626650c61c6811a680","tests/fixtures/invalid/table/append-with-dotted-keys-2.stderr":"7b0ce2aee2d44c234a14bab3fb339661f00b3e3bd39784f3ae7058f3a48fbdc7","tests/fixtures/invalid/table/array-empty.stderr":"e3cd186fd3a22b55fb7e18c6247f91c8da4b61396bde01ab970847aba933b80d","tests/fixtures/invalid/table/array-implicit.stderr":"dcff7e1489ff2a416a5945891c7007152ebbb8cd028baff4439ad2167a1f5976","tests/fixtures/invalid/table/array-no-close-1.stderr":"a816d34d85c8a0ced8cb9315723ea1483711a6f19f6e2e2c96a409d7d43c6fe0","tests/fixtures/invalid/table/array-no-close-2.stderr":"b07a23ed41ea508abc35fcaaad1b38d30705ad874e62ecfb6affbffb39ff4487","tests/fixtures/invalid/table/duplicate-key-dotted-array.stderr":"abfbf76b0555af439f38404ff1f4b2736a759f1bfca347f5eeb8a3754312e835","tests/fixtures/invalid/table/duplicate-key-dotted-table.stderr":"a55218213d972554ab52867926d6c108a308c009b8be13e05578cba1a8638f67","tests/fixtures/invalid/table/duplicate-key-dotted-table2.stderr":"ebabea3cc176fc9f85083f697fd9d6ef97d7230e369be4c504e78c00a82672de","tests/fixtures/invalid/table/duplicate-key-table.stderr":"58598e44d8ab8e8fcaa0f9ddbe2415013ade6965db761d99162312cde1e56fd8","tests/fixtures/invalid/table/duplicate-table-array.stderr":"4079ad8885e7fdd2a0674c31767efb16350ac5906ea5f71f980e06b804a515ba","tests/fixtures/invalid/table/duplicate-table-array2.stderr":"9b647eef2e77b8538ea32d0533b80c870fa4173f0a5b151399c84d7edde3f31a","tests/fixtures/invalid/table/duplicate.stderr":"f537ae526887a2288b943ec1b2d3ee31bf0c9193feaa27bb9e8c447e104ece6d","tests/fixtures/invalid/table/empty-implicit-table.stderr":"b331fd776de53639f8b6f9740f0922ca97aa3919ea365904829dbd61e39a58d4","tests/fixtures/invalid/table/empty.stderr":"bc6c1109c2dea2c69becd952c68f7ced16d983051a9a0b32af1868349e4f2f40","tests/fixtures/invalid/table/equals-sign.stderr":"93ab20fbe68fd87648a4ee58d8af26ea7224818f605fa4cdb2ec5aaad652d050","tests/fixtures/invalid/table/llbrace.stderr":"90b78fd64904ea772687d8fea6df07b684a6d278a47aab242eeace8547921a18","tests/fixtures/invalid/table/nested-brackets-close.stderr":"85435c519a813544af5e686cd89f0a7c2944ab30806ee6fc3632266a03801a51","tests/fixtures/invalid/table/nested-brackets-open.stderr":"2bbca8c47edf49cb4541d869ec198beb5167bd9ea1e40d7daa461f03c5554293","tests/fixtures/invalid/table/no-close-1.stderr":"6e1bbe0ba02c31cd687a9dc8fe0fde2ce1c0cda31aec651dff58f41d8aa7d62a","tests/fixtures/invalid/table/no-close-2.stderr":"3e35a0d431a1056285034b84870455d508ee26539d5a1ff460373507f081b813","tests/fixtures/invalid/table/no-close-3.stderr":"68ce4165d0133af527af7c56fd1af4e652af27cc221f1051a8073e7192a78a20","tests/fixtures/invalid/table/no-close-4.stderr":"52d8e13b39b9507ef37a65c0eb779e89ff7d00fd4a70245d44225a35200a6ae7","tests/fixtures/invalid/table/no-close-5.stderr":"7c3f3c078dbeffcf3b55d3ba811d89fbdd19c1d2ae62b290224003ad26863a00","tests/fixtures/invalid/table/overwrite-array-in-parent.stderr":"f4f94892b9268181ad4e5d64337ec3bd11d0b1a49add7a7c52a0a949f511e79e","tests/fixtures/invalid/table/overwrite-bool-with-array.stderr":"1773dc739ded58c66dadc101668fde5877ba018268dc3b124a4b788b0a0f1c68","tests/fixtures/invalid/table/overwrite-with-deep-table.stderr":"7e1c743c45e4919cab3d36cff96a2165f15cd749411d8c9e758c7ccb7d972893","tests/fixtures/invalid/table/redefine-1.stderr":"a8795ab0ce678bea6299751633e08747b862a46c8f4546094cd1e5f643715202","tests/fixtures/invalid/table/redefine-2.stderr":"dbd674695c75ab83513cf0207304e7ec84565d6dac607e8da85eb9cf41475806","tests/fixtures/invalid/table/redefine-3.stderr":"e161dd3883e7d38e558d296898b637a09ff287cf35a8d921577693fd3c7a2770","tests/fixtures/invalid/table/rrbrace.stderr":"412d28cdee519340465a0e764529acffd5947f566d8b87d69a1a9890845e3747","tests/fixtures/invalid/table/super-twice.stderr":"c9d37817b5e5afd36a2ef3f12190ccf512217e5fdbb78d075d873ca2d2a90f98","tests/fixtures/invalid/table/text-after-table.stderr":"accc6467f519ab738eaa646c255d527ae47c4958231db59b2a9ef16549dc2fbe","tests/fixtures/invalid/table/whitespace.stderr":"73d125e308a219ce5aba0d45ea17b6247eda746e76740f640066b891092efd16","tests/fixtures/invalid/table/with-pound.stderr":"eb3fd32c944de502852f2e7b453f3f56849e7fbdbe9060863f9d65e308bd7f2d","tests/invalid.rs":"ca167500621d70ada023990cf7df4acb329f3ef75964b4d7d818d7b43d82f4c9","tests/testsuite/convert.rs":"5fb95b3296e209ad317f47c9b6b989d432a425e2ec6178c0f22c122a7a0f782a","tests/testsuite/datetime.rs":"105b30cb4cf522c7ff955e8194cb9cd9cf6ec7fdaeb4f316d5504c4868d9c0d8","tests/testsuite/edit.rs":"2a61ae478fcd687b9259f50fc828f599e9eae69a429136cb58cc3af044d96464","tests/testsuite/float.rs":"cc59fbdff449f40727c3032cf9646e28abb420e6990556cbb5dd5cc71cdb910c","tests/testsuite/invalid.rs":"a5ed3dea64d13d9e65bf5166b344ae62fd1ee6a5ab55c4ade2bd48e7e6345db8","tests/testsuite/main.rs":"c85d415a8c20f47f383c051fe7cfe3324947844bee922d6135cabdf63ada8403","tests/testsuite/parse.rs":"2a78b47d13ad27ad728b6bceef1ed8d2c8fe2f695741a03c439aacadf1817f57","tests/testsuite/stackoverflow.rs":"6fd57f327d049e645536f66587c5a09a3b1db1a5775bba87fdd14f3152f771f8"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"f474e15b5e45cb030701e8bb2ee732bd71ed8afe82dfb90f21a51a2a2eb5a4bb","Android.bp":"cbbf49d65b7d5ac5998800f3b995be86cdc1769925411c8015c9dcb322e12a39","Cargo.lock":"f4d23b06ae3b1896c4dda9e6a8cf5d91ac7fd39c6bbbb011de5cd512a7f7aaf9","Cargo.toml":"da166bdd7496436389b616aa50eeb9ab89f53cf2da7c97a7c5d252ca3af06ef3","LICENSE":"7b1c7a1aa99d919e93ff9b6a265d75f2aefea4ae27147ab6dafd8513e64b8a9b","LICENSE-APACHE":"e4493d0badd82c469fd611cf0c31ea8a74cce85c52c4a4c2579e344226f0602e","LICENSE-MIT":"538f704e6bc384de6dd775d81e9df89f9d718f0e2808a24a789eaa03beb129d3","METADATA":"f796074e5e74904daaf2e8f75853978569d6982f9e67ce81d760747f81553e5c","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"f45dac46b26743d4f8fe1358b5c4707f782eb6e487f8a9b8443ef774e9483b78","cargo_embargo.json":"397647bf02fb37ea9b3ec5313148fac22602f64f91ec8ee36cc82f1f0f58b793","examples/visit.rs":"97b91b3931ea8a2096cfb5982ff97c6a3b71698c72698f73293bbfbaaece0e12","patches/LICENSE.patch":"4ba5ca15afd2172bc25ec72dbac0b97c03f67beea425aa56194acd0472acf72f","src/array.rs":"3782a701436c40e4babeaaf40602f6df8ef511294a98522400a1c57ee963d8f8","src/array_of_tables.rs":"ca1b69a942378075e7f5ab0ae09d27bcdb7e78c2c0bb0bbf07e7d5f837338078","src/de/array.rs":"3bed5da8060696518c5e2a3800c221ecdfe3e392350967caa2ee2aab1dd46bed","src/de/datetime.rs":"adcfa9c96a655face16212d4b9be0221723c5916dd4ba0b967c363273c6acf3b","src/de/key.rs":"59cb84b0d3c43fed624d67d3ca01afe042dbe3199d0247f47a6f93097473504e","src/de/mod.rs":"d99680ff57dd8328a782bfd17dbe77cf4d48e132ef59cdb6138e0e90ab5b1ea9","src/de/spanned.rs":"12f7f11ffae52d147dfd6b8e31837d28736cf88d44c93a8071eca736b26414b5","src/de/table.rs":"6993c09ca5885161a51f0802326a8f624b6889342a583bda4e4bada06e59b016","src/de/table_enum.rs":"ad5e7916066904191bd278ed5b7013da7c7d0f50dbf243b9eda5c12129d1e8f5","src/de/value.rs":"019b94c674129abd3348a043c8d7de775fda247debbe185729fff84700067fe1","src/document.rs":"154c96a0a4e80be5ecb9a50725f14d0dec03f59cead324a3e0569ab818e61333","src/encode.rs":"d02199bedb80ce24799db66d214e559fd796d9901bf398ef83b2c5fbf70b1711","src/error.rs":"0d30634003d0ec6c0fc2dca1d1a5cfda178dd9b0caecc162639050b700b3c27d","src/index.rs":"8b5a233305cca7d5c41e5e5cc4fd7be303b44092b55d7167a3fbe595959542a4","src/inline_table.rs":"c26f24ebcb34ffe86388a392147ad28af9c6cbf8e1b2fc504c2b191290f882ed","src/internal_string.rs":"3ccabfff6260df6beba70e9ce29e11e166a06efba0830c657e4d2cd762da6010","src/item.rs":"c43dbc652c4bddeabd9072257652d68f918718a8fc24be94fa988c0fd15c60d4","src/key.rs":"87bb3adaa47e3597fd1e3c37b933ecceec6a61837c3faef6f8109f78b2706119","src/lib.rs":"37bc2c6d236bd543114837a80afc6ffdcacf166e0956bb7a8c0fd35c8fd2f800","src/parser/array.rs":"6745c448bd2468ecc368f51751049fac82752c59d86048a22d8a9ffba00013d7","src/parser/datetime.rs":"ad2078bcc1b9b6c77aebffa1fbcc21401c4a66a95c3ac009a9f2f23aafbee958","src/parser/document.rs":"ea1ada09e7396b61dea578e2f704f1be226c44b7b9b704217ed70bb6fe7cbdce","src/parser/error.rs":"e31e69f00dc9b642713886876573a0e55b438b20a1502481ea9ea26ccb14186a","src/parser/inline_table.rs":"cddd4a09d132be2338aa16ea152c49493301b59554d31d619c9219f0f8beb28f","src/parser/key.rs":"c84b3746cbf7b87f193d28b6b442a0712d6a02274d1c759f7159f8e5102031ef","src/parser/mod.rs":"690f65d437860015d9e757bea1f2eeef15e5f658795623820c67f567a2286d37","src/parser/numbers.rs":"576989e6ad257aeb06463bc800688c39bf585c0cc91b8ae9e0f750a7d3c4e6a4","src/parser/state.rs":"ed8b07352b00f60cd31448adb6b48b7c69828130741928f143ea6fe04af3c938","src/parser/strings.rs":"680878e7170936e1b82d18a289ce13586bffbee0e6d6f4f937c71b996b0ef26d","src/parser/table.rs":"ebe15adb0b447c6590cde2a0726a35450b2bc24cd348b6c1199fdb4cdb434a0d","src/parser/trivia.rs":"b57f101165739ce924e04c47b8878730a12856ccbe3d889413c6cf395244e7f1","src/parser/value.rs":"bc446c4d15af7fff26910d47f60f3caf910af9cf422482e793d2bfdaa4b42d30","src/raw_string.rs":"bdfbe84a498220a6adb015f76609affad0fd352c719a3cb37ff131865b7ab738","src/repr.rs":"ab88409650b979454780d447de8a65aed5c798fd7a4434a5f772d8f83cf9b920","src/ser/array.rs":"ffd5a0e51e324a7defdb345bf625d583cd78f2e45eacd9e4023b638ffb67fd75","src/ser/key.rs":"3f03b4f13c3ec9f7f58d3be1f8ee7dbde9c99dfea143827aca4fffc0d577cd5f","src/ser/map.rs":"d166fc26f6e64bb8ec7076db5cdc50ee77844bc9f1148addbc6cec9da87fafcd","src/ser/mod.rs":"77ed3ed6ac0594e9bed647f00040b009a9802556b33d14824133d508ddfea238","src/ser/pretty.rs":"cee56f209185fd0b78ae4f044f6042bd7e7b7cc8604ca99af8f658d11b88d46c","src/ser/value.rs":"bcbdb731349298b278236fc97ff073a08644d431673be4604de2ba915cedec74","src/table.rs":"52a53f254ef49ac1b05381a3c842f048803b3953efe9970743745dcea664f932","src/value.rs":"b12985099a0542640f73200eaddd073c3024019607c950b00722522497ebae4e","src/visit.rs":"5e7a385a1e659563760e177c5dda97c4b33679df0286c59f43307e7e159c8e04","src/visit_mut.rs":"44b4ceb0e721b7438630ec3a6488619e8380342aee7314ccd1cb2593778a32a3","tests/decoder.rs":"f913f2422aa6924032bc8a6ab77df5954c20ba6c259930af24e83e27cb493f82","tests/decoder_compliance.rs":"74c36d82d52205c3490844ef3df952e294aa92c6e9ee64dff231473072b78b1d","tests/encoder.rs":"32421da7d726611006c2c7198e0da3944ad3b42d7b456a6ebdb894ff90f40280","tests/encoder_compliance.rs":"1d9538ee077af51782213cc3484de5252b8700e242b832735c401fd6343623d6","tests/fixtures/invalid/array/array.stderr":"517c397c7afb8fe855fe9b86a253f620f761b5403fdbb08b219c893f096804c2","tests/fixtures/invalid/array/double-comma-1.stderr":"517c397c7afb8fe855fe9b86a253f620f761b5403fdbb08b219c893f096804c2","tests/fixtures/invalid/array/double-comma-2.stderr":"f8a4c678e4fa5bb4b2dc66064d113bd81beb5e5258046685f967feefa825e8a8","tests/fixtures/invalid/array/extend-defined-aot.stderr":"86cc5eb992fa9add3ad39766d6c6736d30dfd9c464cc516c45dec12bc1c8e8b0","tests/fixtures/invalid/array/extending-table.stderr":"9d666931632f0c02362f102e9edd86916a7de491a83d6dfc68a0c2f87b0794d6","tests/fixtures/invalid/array/missing-separator-1.stderr":"fb21970f88b6f5ede63891c6baf406cfcefea7d48c15c0b4670c71d97c61c3ce","tests/fixtures/invalid/array/missing-separator-2.stderr":"3a807ad6e94c3d70b62696bec39d7a1e558e5771d6c6484a80e89ffde6aa9540","tests/fixtures/invalid/array/no-close-1.stderr":"7c1689bf4779e193a475bc181b622e2dcaa2b02e2c9a052ba953c94d2e48b724","tests/fixtures/invalid/array/no-close-2.stderr":"e3421314255375d50d74e04b527adec9aa1b7cd9a8502120247785a34f0b7368","tests/fixtures/invalid/array/no-close-3.stderr":"ec1d919de5766216b887a34099b2117db7e3e3cfaccd8b295f44268d950df14e","tests/fixtures/invalid/array/no-close-4.stderr":"4372b71512f369cefea355dc044e04483c5f7943028ad5cc0d3514804d3ad18d","tests/fixtures/invalid/array/no-close-5.stderr":"87d9fcd6aae71e0542f70eb8ee2a509f024e66d0b10d03b2f7f697d76b2bb575","tests/fixtures/invalid/array/no-close-6.stderr":"9ae5dc0d71db715990ef8c7e8ded74643c4a451c5abb4c05fe1bd6eae61d20b9","tests/fixtures/invalid/array/no-close-7.stderr":"eae2afb173b6a4d58e65dc94782c1b5079588806396c6b5716fefe911643966a","tests/fixtures/invalid/array/no-close-8.stderr":"feba7ed79aaa5d7b92fca24d09ddfdc1a2198e84a51aa8a4a7578b75e945e002","tests/fixtures/invalid/array/no-close-table-1.stderr":"ff5517cc98a1e0918889defcf594a919f1c52ef508bcc4183114305f2df8cd77","tests/fixtures/invalid/array/no-close-table-2.stderr":"21751532b65a2db6064cee09f15f65d4773c34ed83b7269d09e2ac00e5859278","tests/fixtures/invalid/array/no-comma-1.stderr":"02a1e51cbf040add31472bd7c56723005c89c8b793ef59769cad78d36e4f3139","tests/fixtures/invalid/array/no-comma-2.stderr":"4dc26ed513ceb32d721c4e25a7a1918b9c61bc93d926bbab04975dc1a930fda3","tests/fixtures/invalid/array/no-comma-3.stderr":"f5441aa4804a2ce10fa621304def1c296b4b35bf6e7657a2926d364b9d3d4db9","tests/fixtures/invalid/array/only-comma-1.stderr":"b7d5fe39fb03327a5b55b940f524aa4dbb5548604fbc8786e1e4e502fbf6f16c","tests/fixtures/invalid/array/only-comma-2.stderr":"4a54ddd36885beb51fa70327ecfe81fd6dddc5f1f23ff85a61f30834577b4b2a","tests/fixtures/invalid/array/tables-1.stderr":"2a9ec07bfa4c818d204e8d49c621e13194cffc99c21a75533a8dd3b00ffcfdb2","tests/fixtures/invalid/array/tables-2.stderr":"e04375e46225e12956b22fb717615b6cf33f2064f12a9a6c93074f42adaa5374","tests/fixtures/invalid/array/text-after-array-entries.stderr":"320eb4e7f8ba560da84cac292a9d5de03534f6ca6ff6928e429ae8cd55ec4395","tests/fixtures/invalid/array/text-before-array-separator.stderr":"b37ba17334486b7b16d4b5bde308d2a8c7605429c389e3ec30b02cd975fcf441","tests/fixtures/invalid/array/text-in-array.stderr":"895f20e5d6f2748c16659edbdb4c6e91d84f29e45f8e72d380bb3205f6c9065a","tests/fixtures/invalid/bool/almost-false-with-extra.stderr":"9c06a48312aa0138684e37b8d4193e46ff1badf6ee3d2424d6c6e5f341db8a61","tests/fixtures/invalid/bool/almost-false.stderr":"d3c827c1c521f98b2242a7183c96b00682764889a46666a5dcfb6d0fa7a743c0","tests/fixtures/invalid/bool/almost-true-with-extra.stderr":"9db6045a6e3ba501874de810a16a35f9837647dbab22ea9d8c9d89a7494b983f","tests/fixtures/invalid/bool/almost-true.stderr":"2816a9e9637d3402750f7a35622d738abc83825d706fb4c19d8f007df2396bb8","tests/fixtures/invalid/bool/bool.stderr":"9c06a48312aa0138684e37b8d4193e46ff1badf6ee3d2424d6c6e5f341db8a61","tests/fixtures/invalid/bool/capitalized-false.stderr":"b7e9c3764103c257ceae4f0aeb714ae58917722cb6ac1a1fdb0cde6f9efe1c83","tests/fixtures/invalid/bool/capitalized-true.stderr":"ac46ab983618490106a1bd3263e0e34e54010a014a795184fb722d5ef49aab2d","tests/fixtures/invalid/bool/just-f.stderr":"d3f2bc776b3137f378be5acb9e10e745cd124178d836289fb1b7091ab12556fb","tests/fixtures/invalid/bool/just-t.stderr":"1f6c7ced5fd72596cde8daf3bd05a61c9f0a28bfb612b8b6b0b6de4630d1c0a0","tests/fixtures/invalid/bool/mixed-case-false.stderr":"7d57131577e1c018075c1a83347e80183aa1db1b6aee1027a11cb7f229b05d21","tests/fixtures/invalid/bool/mixed-case-true.stderr":"9cfe3868f18cecce20bc674593f15fe88cb6fb7361f54ffec1616e877f714cf2","tests/fixtures/invalid/bool/mixed-case.stderr":"34738e926e3e5ea99fc9860c0177463699e54b2c48fe11168a6c25bc173802ac","tests/fixtures/invalid/bool/starting-same-false.stderr":"620c142ae095c8da9aa1298a82a42ce1f0f13eda91c66e75e4c5b6f82bee0455","tests/fixtures/invalid/bool/starting-same-true.stderr":"8e1016111cd84601db76df8579ce7aab7fa7a0e013e66d3174cb8bbd80c4764f","tests/fixtures/invalid/bool/wrong-case-false.stderr":"ca86c0166b4992d0255e2c8f5ed2935d58c325965039006f1903c8ddaf549593","tests/fixtures/invalid/bool/wrong-case-true.stderr":"6264fc52c455d04e1f89d0d2726b4325066699595f6120ad4583802642cd6735","tests/fixtures/invalid/control/bare-cr.stderr":"be847ba07cd507752b8124ca13e8d012c666b81cee56be1309815c8d490470c1","tests/fixtures/invalid/control/bare-formfeed.stderr":"18a939f8d8459a4e3a2d9dee5756fa81b3f12a2179e685c35ecd25f4da9d8aef","tests/fixtures/invalid/control/bare-null.stderr":"cd58586e56fa9b00fa17be23dfd531dcf9eb5e10556464decd4b60fdc6eaf72f","tests/fixtures/invalid/control/bare-vertical-tab.stderr":"970f649f9575b1553da1382466eafe16413696203e7ddbaa99a0585a3f0ca157","tests/fixtures/invalid/control/comment-cr.stderr":"7e729bfc9b0c575521c39d763e8ab8f7baf94054e183e59cbe976d840a0f1c62","tests/fixtures/invalid/control/comment-del.stderr":"5c41d37aad38933267d8d8fe80d9600650627dbe3433b8ac876ca63bd086094b","tests/fixtures/invalid/control/comment-ff.stderr":"4a7eaa53d883dcab7f23b09d33794a687b5d0c7777ab172a0bd81f9503878762","tests/fixtures/invalid/control/comment-lf.stderr":"a24258d06bc41156b92ab14fac3793c85d706e74d5c280337c4ec0c4f80f92ff","tests/fixtures/invalid/control/comment-null.stderr":"afcf5791d6fde718cd6a2b514483d45d2355bf08f9fa392d75c7192bc390913e","tests/fixtures/invalid/control/comment-us.stderr":"280fbd7dc323d8841d5b9fa821f48061fc01a968bc1f5893fdac30fc17afaa3d","tests/fixtures/invalid/control/control.stderr":"07adf02cb547ed379a402121e4db738daa698358e53c2fa852c617fb766b41fc","tests/fixtures/invalid/control/multi-cr.stderr":"1b70b31b5c10eac9a52cad0b7739bbe5b9a74c9cce4ba105143c622a33164c14","tests/fixtures/invalid/control/multi-del.stderr":"6a7f52409dbf8b5bc1097fec2d1330310c97d78eb3ea2027f219b25e519ec270","tests/fixtures/invalid/control/multi-lf.stderr":"e723fdaf9e19aef9a0eee8a919e5986822c25ac1131e24422e9fabc0887ccc63","tests/fixtures/invalid/control/multi-null.stderr":"ab3e944c835a74ebab8c56c39c3ba6496e8d4d2b03f324a66e2e511ac8b7fd10","tests/fixtures/invalid/control/multi-us.stderr":"1df029e738c4cb78a68b360fc131fb6d9f5b93f78e292a27aaf4c40e8aa3d842","tests/fixtures/invalid/control/rawmulti-cd.stderr":"fbadd57208404b2116c9cc16f36b80e1ab08276d7838e17e4bd48a8df7e194d2","tests/fixtures/invalid/control/rawmulti-del.stderr":"ee50d46a73bfcd85f7b3b677d4148cf2390e5ea13b59a6bc81dacea3e049ab33","tests/fixtures/invalid/control/rawmulti-lf.stderr":"6cae2d709ae5ee6afc355322388d8b57d8e9674929d71af5b7761bab5d751cb9","tests/fixtures/invalid/control/rawmulti-null.stderr":"0efaa11fc391b16d27e1de09027171e8d379d3ffddd2489045ba10912ebccb89","tests/fixtures/invalid/control/rawmulti-us.stderr":"31a28ca07db60771f7787f8ef30fa160ce3715df28532bc9892ec15e380c36c1","tests/fixtures/invalid/control/rawstring-cr.stderr":"32819db35fde8ef9ee282557740c8b2dd077c0bcf421614fb06db18d732af57e","tests/fixtures/invalid/control/rawstring-del.stderr":"8a7828be56c7d941c6879698be4825fc1f9b75766898e782675d860581e7393e","tests/fixtures/invalid/control/rawstring-lf.stderr":"1d01b15b19024ce4c36866f025ed6adc4f3c3bac1a2b5fdf4ea79471dc5c9b49","tests/fixtures/invalid/control/rawstring-null.stderr":"245b39b3943e776fb5cb2d52497e0fb301420c36f7f5b47df9980aa05496ab5b","tests/fixtures/invalid/control/rawstring-us.stderr":"7fb0ed283f42d3b1cc3852698b6047c2975d2c780eebe14f65d2b3d11d9f0bad","tests/fixtures/invalid/control/string-bs.stderr":"0a12dfbbf6161d24f29d78679928a4e835d47558344773c2d585a2201bb043ad","tests/fixtures/invalid/control/string-cr.stderr":"cf5970ede487e7fd692baa45247d55df70b8207f8d67df6c2e55eb9d4ef9352b","tests/fixtures/invalid/control/string-del.stderr":"35c65f3ceb74b80d4a5d8109f61f099bdf640379dc9751fd7678447a33eaec78","tests/fixtures/invalid/control/string-lf.stderr":"ede4b6df958cac81b05525f25b1ac2c1435eae895f545dac57a553a7840fc8d7","tests/fixtures/invalid/control/string-null.stderr":"c30aa237809c03dee02d36a1fc4a02d6f184bd425bfc2accda6b3fa1bb55fe1a","tests/fixtures/invalid/control/string-us.stderr":"7d922c62bf118c23b85991935b8b4c7a113d39d0092d13d22be1e0ad7ed20a31","tests/fixtures/invalid/datetime/feb-29.stderr":"743e7bcaa21ce6a8e7f033b56de7a0f3e7dd54ea84bf293e044af4e192f693bf","tests/fixtures/invalid/datetime/feb-30.stderr":"744e980d51f19b89a203192eae4f88e5ecba71853e006ab1c52988818584507c","tests/fixtures/invalid/datetime/hour-over.stderr":"0aabf978f79acc6b4d0d3d6a9b05833aa65d418d71aa5cbbd71035797adc1b0e","tests/fixtures/invalid/datetime/mday-over.stderr":"76f70be6d3ca444339aa15f4e6243b784a4c414fde0cd932b20c182266276782","tests/fixtures/invalid/datetime/mday-under.stderr":"54456d2ec892deb33b596cd82d400bdc6777cd2b5c643e45556b9646057f7d55","tests/fixtures/invalid/datetime/minute-over.stderr":"ffa678092cb67d6848910218d9a5bd46f5a308b7bce2269b8b6e46c2ec019dd0","tests/fixtures/invalid/datetime/month-over.stderr":"906596048b7dbbae0630ec1216827a95ce562a09f98704595d27c3491cdfc7ee","tests/fixtures/invalid/datetime/month-under.stderr":"c073c25ef0a3aa10b1d29ac28df9c9207143ffd595680ff633400d076a76dde7","tests/fixtures/invalid/datetime/no-leads-month.stderr":"4f5066ed79bcd945215a57f55ef230f47dd64e2ec55438a7ee08bc3f7067e318","tests/fixtures/invalid/datetime/no-leads-with-milli.stderr":"ffce1f8e7bb51130705ebb7ba8e9ef007de9a9e7f33b476e4fefb15a030d2e1d","tests/fixtures/invalid/datetime/no-leads.stderr":"4f5066ed79bcd945215a57f55ef230f47dd64e2ec55438a7ee08bc3f7067e318","tests/fixtures/invalid/datetime/no-secs.stderr":"8c504dfaa1dfef25b78a8548509b3139f92e3e04bcb848a1e04f7529e169ad63","tests/fixtures/invalid/datetime/no-t.stderr":"ec4852cfa295ad2944101831bf40489e6c22b5a33fdb3faa5347e2d18c25c227","tests/fixtures/invalid/datetime/offset-overflow-hour.stderr":"7a4250381a915c58e3aa81cb3afb59fc4163b7d25cee0a6625b615b4fe5e0fad","tests/fixtures/invalid/datetime/offset-overflow-minute.stderr":"6721e5fb29bb4ab20711eeb3fe42a6f1fad4b279075296c0a97124d75dac08af","tests/fixtures/invalid/datetime/second-over.stderr":"ba720829aee7506625c354e5f53dbd6b67ec351061df1e32b49cb11ea69bf52e","tests/fixtures/invalid/datetime/time-no-leads.stderr":"5117ac62dd754a203f2fd6e5163a2ce352776bee513219252cec77c0ed71bbbb","tests/fixtures/invalid/datetime/y10k.stderr":"ac3db71c252bb0b2f0bdc8a9d734ca60289afecfb5a66309a895b54c7d8e61f7","tests/fixtures/invalid/encoding/bad-codepoint.stderr":"64244879fa48b9c4f2f7b219325d4c308772c032b7005edd2ee4d771b5448bc1","tests/fixtures/invalid/encoding/bad-utf8-at-end.stderr":"d55ab89a2f111bbfffd362928dbe3a1dafe7f8f37a04e1c56307efe4f4ea14d8","tests/fixtures/invalid/encoding/bad-utf8-in-comment.stderr":"52e5ea4555b208f3ce91debc99e01dacc6e706381fb274259730b0a82f041660","tests/fixtures/invalid/encoding/bad-utf8-in-multiline-literal.stderr":"e22a4a60afcbf41351abb9d4d61a4c5d6dce246f98fedbccd33557eea8596524","tests/fixtures/invalid/encoding/bad-utf8-in-multiline.stderr":"e22a4a60afcbf41351abb9d4d61a4c5d6dce246f98fedbccd33557eea8596524","tests/fixtures/invalid/encoding/bad-utf8-in-string-literal.stderr":"eea9032a7d46e496ff8f6d4a95344b4d7660adb0fc715f652aefea54ce72d85d","tests/fixtures/invalid/encoding/bad-utf8-in-string.stderr":"eea9032a7d46e496ff8f6d4a95344b4d7660adb0fc715f652aefea54ce72d85d","tests/fixtures/invalid/encoding/bom-not-at-start-1.stderr":"dc9eb6707b252b77ee0c30bc471a4bd58308b31bfc892e2fd5c3995458e41b97","tests/fixtures/invalid/encoding/bom-not-at-start-2.stderr":"6570c6feea522cf017c8dcdc1e4b76cfeef2092bc3b938af0140e171817a8311","tests/fixtures/invalid/encoding/utf16-bom.stderr":"d9c278ec8c5a422f4553180ea76dd223278acc0d0ecc5f725235db6b379e5b99","tests/fixtures/invalid/encoding/utf16-comment.stderr":"2f2c4ffa18d08721d707b7083706efe5394e9fdd3504791a52949d76b7442e67","tests/fixtures/invalid/encoding/utf16-key.stderr":"8a8465ae12c6f3065edc3b237b7066bc8cf03b95a22403c8c5a1b1a5f085dbad","tests/fixtures/invalid/float/double-point-1.stderr":"817b02c19d53f02107569e7ddf9b38334cd85edd0a452795baaf6d5c0ffbbf63","tests/fixtures/invalid/float/double-point-2.stderr":"8b5a07c41087342d5f8bfaa8998465dae954cad11d93f01f4ed4a5541694e7d4","tests/fixtures/invalid/float/exp-double-e-1.stderr":"657c856a8d474cec0d557f4a5401456966bc82c8061a092977f4ae0cb71fb04f","tests/fixtures/invalid/float/exp-double-e-2.stderr":"ad101779127abb35a6e35020a4818c1622ae5433208bad57f1daf113357f6ab9","tests/fixtures/invalid/float/exp-double-us.stderr":"e6e53e58b5deefc9e50e6eca91423881b682bcf8528bfa0386158aad9458ae74","tests/fixtures/invalid/float/exp-leading-us.stderr":"5e45eeabdcf142ebd5b5ee2b77e2520c5ae7e35e943034e12c84095f51a1141e","tests/fixtures/invalid/float/exp-point-1.stderr":"2d4202fc806e23ce05f8bb3e63e31d1b4874875b018f28bd2b59c8714aa70cd2","tests/fixtures/invalid/float/exp-point-2.stderr":"d85145654b358cb63f44ea754c88f29a073fe5f637d07b279a986ddc0c347df4","tests/fixtures/invalid/float/exp-point-3.stderr":"3fb7d6151b522057a2b15137daa916681220cf575dec560f084d3c9c2dae1f3c","tests/fixtures/invalid/float/exp-trailing-us-1.stderr":"71aef4dde4dc2371702025127b9802540765a0c0db6f96156bf6542c574b30b7","tests/fixtures/invalid/float/exp-trailing-us-2.stderr":"c63f97484c7c498b01fef93984beadb9311d884ce5b7856ab1301cc398ae9c4c","tests/fixtures/invalid/float/exp-trailing-us.stderr":"f5bbc5fdf2dd018c031ca5c79edd7f16a0da25676bed96ffc0682401497ef362","tests/fixtures/invalid/float/float.stderr":"511c7e989b6e4768f089634036848acc896439a06df83a97fbcb92ade1554e98","tests/fixtures/invalid/float/inf-capital.stderr":"d493ce9b11dae0dea40a2c8c7666125031d5323ab82bf1b8af997049d60072fa","tests/fixtures/invalid/float/inf-incomplete-1.stderr":"6090e21b98dc6df8e67962c8458ac773bf3dd4b78ae4f4453dfbe01657401a7c","tests/fixtures/invalid/float/inf-incomplete-2.stderr":"abc3855709f099a9023f0d3e305edb8c97570223f889431dad32e21e83747d6c","tests/fixtures/invalid/float/inf-incomplete-3.stderr":"9fb99f8ca91b9fecb86ce27e1b4fa6ca05a2d5c0ab039972d4d2a7dcf2c6228f","tests/fixtures/invalid/float/inf_underscore.stderr":"8e7202c508c9d6e35410e4ff89bee3a5db4a88367e69e210fd4ae4f9bf602286","tests/fixtures/invalid/float/leading-point-neg.stderr":"21e3e8ea4c817ca286b1ee6dd359c91b6e8ffcb376bed393e03a29e55d7a6a61","tests/fixtures/invalid/float/leading-point-plus.stderr":"ff0f7a8a5cdccfc7e6d3e6b1381643b759f5aeff5546bfd2fd4dd29f8f1b1551","tests/fixtures/invalid/float/leading-point.stderr":"5c7590482ac55bb0d11eb9e1291a80d8c891ed1949adfc64e81b75bc5f8c8b06","tests/fixtures/invalid/float/leading-us.stderr":"67ce463deceabfa0dd4eeebfa68d72eb53fd235455d6181b4c8597c6507e0ee7","tests/fixtures/invalid/float/leading-zero-neg.stderr":"16b6cf97171ec9f85a36e09a1ef39087eff3a0c441a9f9d96c43a9d0fdb5ee38","tests/fixtures/invalid/float/leading-zero-plus.stderr":"f08e1416644c743b3463342313f023982051e992756e9784229b1bcfc1e19aaf","tests/fixtures/invalid/float/leading-zero.stderr":"511c7e989b6e4768f089634036848acc896439a06df83a97fbcb92ade1554e98","tests/fixtures/invalid/float/nan-capital.stderr":"66fe63db08d9ee24e8a90f4e2b5f2c15fb3f35dfb7eb81ffe760f8957967370b","tests/fixtures/invalid/float/nan-incomplete-1.stderr":"138d0d28aa5e90b9fb344c743478c0b6be79492ecac856d71e239af5f6e1aa99","tests/fixtures/invalid/float/nan-incomplete-2.stderr":"31f200fbfc6e4c8d06f58960dd7349f650af86d22a1f0d6fd8c4df46bb4a0b3a","tests/fixtures/invalid/float/nan-incomplete-3.stderr":"3b16f8d82cdf18df21bba1e83d2aa33e175ab5a37fd5f3214b1be84a07acab10","tests/fixtures/invalid/float/nan_underscore.stderr":"4a30507eaf227e8c8e2bbda6d7b3cb4fdcf793216981d1f9302cb2b049e95d58","tests/fixtures/invalid/float/trailing-point-min.stderr":"0b5c7385ced699074d6042b5fa0ea9266ea64a191dd0e43466ca71a2ceaba76b","tests/fixtures/invalid/float/trailing-point-plus.stderr":"856edc036309660c223b1cac7c99d9d6b61bb4b49e592410c3f308fb0a678800","tests/fixtures/invalid/float/trailing-point.stderr":"5241f4525c36c1cde19d520aacdfb7db1bdea07710d2621d2a60ac7b1d1e70a9","tests/fixtures/invalid/float/trailing-us-exp-1.stderr":"42a7b0d43ef2b548c2199a4c9ade5b30c28b91a625d6bac6f57d721b4fff2848","tests/fixtures/invalid/float/trailing-us-exp-2.stderr":"13c1374bbf3f5e3c762b6246dc8ea1f5e619b3edd6d66a2e37ba51fe3ba0acca","tests/fixtures/invalid/float/trailing-us.stderr":"4103e29434d2c323c7461903cbe27dc0a5a70c9907d2d47ee31eaba3f4d51b2e","tests/fixtures/invalid/float/us-after-point.stderr":"2d0ef75d124b8ff10a48639d2f837a13cb18ae66891c9fc3709199ff724c1af3","tests/fixtures/invalid/float/us-before-point.stderr":"844c0d11f60522784f6f22f7f3d418c7e6f16cd96cafa55f542996974fd267c0","tests/fixtures/invalid/inline-table/bad-key-syntax.stderr":"9fe839d2c861b2026ffcbb37a7aaca3498df90f66da6d402d5ab96ff357de453","tests/fixtures/invalid/inline-table/double-comma.stderr":"67e92c9f776a5bd04f908a79c6f2e8c146ebb6072129ab7fbd9606ed8d56fc44","tests/fixtures/invalid/inline-table/duplicate-key-1.stderr":"3eded78c2d76205b8f516e180f8c8849d6ba34f04df72c5ffac319264d16f40f","tests/fixtures/invalid/inline-table/duplicate-key-2.stderr":"a166a188a3682704cf70153666734fe63ccc23075265d0d20ebfa25ba7a633fc","tests/fixtures/invalid/inline-table/duplicate-key-3.stderr":"3e4cad3fdeda14976a0e6193465ac7b0c052a075b55a4dabb95ec2a03d7ef532","tests/fixtures/invalid/inline-table/duplicate-key-4.stderr":"2ffe3212ff7d4dbdd7f41f0c0d037599d223c1876af8c9fa25770431abd7c6f7","tests/fixtures/invalid/inline-table/empty-1.stderr":"ee1624b0ebfc28edb9af042a4926d6b5b62fe7b6265544b158b577bfab1f0866","tests/fixtures/invalid/inline-table/empty-2.stderr":"70c376a4f2368d3586184e70110115525f35cfef2fd15bd2c0a9579a88868cd2","tests/fixtures/invalid/inline-table/empty-3.stderr":"fb05dcafd2b5e290ea8de8d0b8d9ad0fc58452d75c537e8482a2fa7fb714126c","tests/fixtures/invalid/inline-table/linebreak-1.stderr":"016daca1cf56861089265156af1ef41772ed0c5fad6848cbf8c8c91ddf748ade","tests/fixtures/invalid/inline-table/linebreak-2.stderr":"b259282b6bc1a9f9c96de41d2c5b0f27b9d8648ec8984aeb6760cc15ff5714d3","tests/fixtures/invalid/inline-table/linebreak-3.stderr":"59b34b9c76c57e02df83c8ce97b1f3e856026cbb33416d7335f287222e11ec35","tests/fixtures/invalid/inline-table/linebreak-4.stderr":"8a78e7240345fc8b850cd54d3f1c74187e4abcbc65b85e3b1da79ebf3e139d70","tests/fixtures/invalid/inline-table/no-close-1.stderr":"6da0e9a2d2d272d199745c704aea6bf317921d8ab005882a3de793e6ff87e863","tests/fixtures/invalid/inline-table/no-close-2.stderr":"eef11b2fc399ca9b7a397c5366a9b7a9021e783cb189d68fd5c29cc607a5f4ab","tests/fixtures/invalid/inline-table/no-comma-1.stderr":"d2a33a967d517c9690b90845347d26d8845c997ba4ed61c640a58cdf919d7c7c","tests/fixtures/invalid/inline-table/no-comma-2.stderr":"c4452ed96f19979b36019c568fd8c73e72db2711a87d17cc8f62362b88598b30","tests/fixtures/invalid/inline-table/overwrite-01.stderr":"4e47f343291068cb0164fa2d48be2c3257458ee7467cd60d664bfbb83713f2f9","tests/fixtures/invalid/inline-table/overwrite-02.stderr":"40718dec554fde10db86ab240f5071b35ba564503962938f7b9464341e7b27f1","tests/fixtures/invalid/inline-table/overwrite-03.stderr":"0318c1d4713cea361a857d382bc8b150dbb1ba8f8e613df472e936bab38485a9","tests/fixtures/invalid/inline-table/overwrite-04.stderr":"9e55cced7664c821c073937321880bf75150affa44d4363c774f5834bbac7470","tests/fixtures/invalid/inline-table/overwrite-05.stderr":"59a0ef41c6059994212244c16a8808b4ac721c710f1364639f7116d43a7a8b26","tests/fixtures/invalid/inline-table/overwrite-06.stderr":"c2a6060831445d4143254c6908468bb04cb5be501161028429467a2dbb68e235","tests/fixtures/invalid/inline-table/overwrite-07.stderr":"cbe9f4d1b428e658591e077790986a6589385474efd2ae9f488b6003715198ca","tests/fixtures/invalid/inline-table/overwrite-08.stderr":"d85341eff5c078131c65c801a259d0909921c541ea5a8eb75e752e5d174092f4","tests/fixtures/invalid/inline-table/overwrite-09.stderr":"99c73b045436b330b4f6570bc1a40726dec71a524e44f6b63a8c55ceec4e5f4a","tests/fixtures/invalid/inline-table/overwrite-10.stderr":"746d1cfde95d2c5fd748e4b95fb95383e014a00f73c03625f7ebefa52243e815","tests/fixtures/invalid/inline-table/trailing-comma.stderr":"079ea824e27cae66b32160b99d6fd75b38514960c440ef14274528fabacc98d3","tests/fixtures/invalid/integer/capital-bin.stderr":"9224535b369db24906be0bbbac4ffa80d1b570db8135ce7f58202956cf889857","tests/fixtures/invalid/integer/capital-hex.stderr":"ee731f89283c425e7c54cabd439651cc4926c1d721ee92ec809b123521ff7c91","tests/fixtures/invalid/integer/capital-oct.stderr":"52005625e8e51afa09a5163c9eaa010e09830727128d68a1e48fa600be6823e5","tests/fixtures/invalid/integer/double-sign-nex.stderr":"819ab532a90faab548580eb0a959199466acfcd04d29d77cd0af7acba509cb1c","tests/fixtures/invalid/integer/double-sign-plus.stderr":"94f6b7c5dbe458ebb1504b7672a36c9d115e17b0e629dcfe47360803c9cfa923","tests/fixtures/invalid/integer/double-us.stderr":"c9a377fc0417b12123e9cdc4b2fe69aade63e532e20e65f279d7b4460ad05e49","tests/fixtures/invalid/integer/incomplete-bin.stderr":"143b9a89feb99b5a420bdc6aeff919ebf6d53bc011f31afbee5b8677a8ac84bd","tests/fixtures/invalid/integer/incomplete-hex.stderr":"b246cd8117362f5451c60298a318d6f93233bf9959c73cdd7a6d590d226291c2","tests/fixtures/invalid/integer/incomplete-oct.stderr":"4cdbcf8ce0a743a9442321a15c01670109debeed6a9712a95d2cb95a9debc469","tests/fixtures/invalid/integer/integer.stderr":"1afca937abf7f267f8c8e9d2112309d1aa2dc90ee1e2d4328f5a30b19ab70941","tests/fixtures/invalid/integer/invalid-bin.stderr":"e94521b61ae2bee5032073007ddd027945723f8c7faf3b4523ba1f8e9604160e","tests/fixtures/invalid/integer/invalid-hex-1.stderr":"5873555438d114b5d1eb323cdf95a651bef7f95b4c1259d657fdeb993350a2be","tests/fixtures/invalid/integer/invalid-hex-2.stderr":"fac8c488203e501d3ff383313f8a1beebfc5ff5676eb6624ca9a774094105bdb","tests/fixtures/invalid/integer/invalid-hex.stderr":"521096bb5d0c10e420a452adb133eecee4e153809a3c29822e64569e4ecf2779","tests/fixtures/invalid/integer/invalid-oct.stderr":"cf940d8abf0a98dbf4b139089cb964964b30e6fbdf89d9a11bfe94b06bbb3547","tests/fixtures/invalid/integer/leading-us-bin.stderr":"34a6f856274435b07ed74ddcbfd0c471920b418858fc2685eacacf8b814d2222","tests/fixtures/invalid/integer/leading-us-hex.stderr":"a468c4d3ef104f5f575ef61e01a2889bd52112fe00917380d9fe094e7f1f0625","tests/fixtures/invalid/integer/leading-us-oct.stderr":"c6600b9cac7b6a935923b951b24051f2494fdc2d351c0f109f63da546a280315","tests/fixtures/invalid/integer/leading-us.stderr":"4c156a62e39d8404bcaea001ad347aa3366ad956899fb385492203d17da9b34e","tests/fixtures/invalid/integer/leading-zero-1.stderr":"1afca937abf7f267f8c8e9d2112309d1aa2dc90ee1e2d4328f5a30b19ab70941","tests/fixtures/invalid/integer/leading-zero-2.stderr":"4e4ce0d6714fb455def4a586c076072bc8d83b87f3b62122611b6e62df5fe532","tests/fixtures/invalid/integer/leading-zero-3.stderr":"d28a4f3b59454eb4a5756d9878b54c03a154aa3af32a9bc4b746fa8d4c842454","tests/fixtures/invalid/integer/leading-zero-sign-1.stderr":"b84ffda89f4797a517f8488b59ebd795fa42e59be7719b3883b2290c8f1060db","tests/fixtures/invalid/integer/leading-zero-sign-2.stderr":"3484e932794a500ed92322323032f7ca2c95fda1af3586fe3d9d5d0cdcdc41e3","tests/fixtures/invalid/integer/leading-zero-sign-3.stderr":"1f7afe7069d13990918c68a32ba46a9e17a305ced699c94cba72b01eec5aed93","tests/fixtures/invalid/integer/negative-bin.stderr":"b3825a06a49dc04be695629fb45ad33a6882ea73821c3c42db7dc9ebc878b5e8","tests/fixtures/invalid/integer/negative-hex.stderr":"f54b8ecd33db30a04ff9312f4b7a42d06ce8c276062a56bdf3773b10e267036c","tests/fixtures/invalid/integer/negative-oct.stderr":"12a30a960af847a2762336b6b0569d603c84dcb31a694bc93a136fa503c298e6","tests/fixtures/invalid/integer/positive-bin.stderr":"f4f91449d9863cfb107f4f174e12e64b01e676c36bab77a431078c9a0fda6e94","tests/fixtures/invalid/integer/positive-hex.stderr":"1ea707ef22ca30bd0ce9becdd4c6c3f04a65a79c153ec38875b4a8626c62dbf2","tests/fixtures/invalid/integer/positive-oct.stderr":"f033b6c19e3f1f04c69baa8ebc4156ac913be0428e85fd474a99d57298c23e75","tests/fixtures/invalid/integer/text-after-integer.stderr":"581885f158d84ac387b8bfb483d4bb0593e23bbf251a63a542ec81630ffd0bd5","tests/fixtures/invalid/integer/trailing-us-bin.stderr":"7e5928cd15695fa35403a024856f64cbbf7a056e8079f76d1923e38401086e41","tests/fixtures/invalid/integer/trailing-us-hex.stderr":"83d3672baed59c1b0ca8b27400b469cd18d2c0790ececd9d5dd72e283ceb4584","tests/fixtures/invalid/integer/trailing-us-oct.stderr":"e048d0b7900704573a076501b5fa48289dbaec4ebb279f10775ab0b8c5060dd6","tests/fixtures/invalid/integer/trailing-us.stderr":"9e46f25d350e02fa57100d87917d9e6a961907df89955c01bbd2bb473f807297","tests/fixtures/invalid/integer/us-after-bin.stderr":"e833ae217e60edfe08f5c76fbbd8ec2bf6de9b41b16905e474791f1b5bc4695f","tests/fixtures/invalid/integer/us-after-hex.stderr":"d5cd357750289db24ddba5d8278eef6073f591a05bb495096e8f6214626187e7","tests/fixtures/invalid/integer/us-after-oct.stderr":"7254481b53b8602413ede570876963fb6ea9c2699adb986ee17023cabf5916fb","tests/fixtures/invalid/key/after-array.stderr":"8489fa15adf8e674687aef4a8ef8acc10235ada0e7a3b2fa17aaeae9f9e02189","tests/fixtures/invalid/key/after-table.stderr":"d1856cc75dc16c95c045ae4c05982448e3c2680057cc1e08c09ebe68ffbb35c8","tests/fixtures/invalid/key/after-value.stderr":"00398c3f1305f387b9ff9f17dcc76061350a800e652e00ff2f3892993c42ea33","tests/fixtures/invalid/key/bare-invalid-character.stderr":"19220d575145a93b522e39f59ee63fe0418194fa2b80d9649916d4b8df556147","tests/fixtures/invalid/key/dotted-redefine-table-1.stderr":"169e40f3d1be87cf099a9e7c6e39175ec0fd701c933b427cefc300d2e854aa07","tests/fixtures/invalid/key/dotted-redefine-table-2.stderr":"72d632c3572aa914603d154152e10157d4db3d90db0da0eca88baf1416b3e29f","tests/fixtures/invalid/key/duplicate-keys-1.stderr":"f4207dea6f110318da0c9c71b3957c8182b133bda91911804a98bac71c025cc2","tests/fixtures/invalid/key/duplicate-keys-2.stderr":"a5e1fb5b90daf5b7e659ed76c82dd0ff6cf2a09e3578c98bdf907bc7801614ce","tests/fixtures/invalid/key/duplicate-keys-3.stderr":"c8d91fe50b91ad091430b9699aea28a42b7822373c43fa064900721df066d45c","tests/fixtures/invalid/key/duplicate-keys-4.stderr":"ad171657bb84292443753bdf9f2963cb268a45dee78ca4f39dd474b6698fb020","tests/fixtures/invalid/key/empty.stderr":"0da32834de6e29b7254260b51b5b26f1537111088f2dfcc7085cf717aa9c7b42","tests/fixtures/invalid/key/end-in-escape.stderr":"f8710ef2b803f053d1c2f85204f7d9beadc485c4a5525d1c17d8c8cc228c0eca","tests/fixtures/invalid/key/escape.stderr":"11c5f871342cd20a90270eb3cc47c06fb186e7493d27026f5936e78c0bd088b5","tests/fixtures/invalid/key/hash.stderr":"55122578bd7c090a641a0aaf980aebe3fd2c1fcf820da4d72408bfce527a5293","tests/fixtures/invalid/key/newline-1.stderr":"32c0795720a45125ff56a3e0a20ce62c2a2174fc02f44f2dd6620db15bc56547","tests/fixtures/invalid/key/newline-2.stderr":"f2d74552b3489eb7f89ec8f4cfb5084ac21ad338ef2a0d320a57c0eca30fe5bb","tests/fixtures/invalid/key/newline-3.stderr":"672d411c92fcfdb495b41a15e6cebf0e73d4484e6a0902772b9937f5ebf35c61","tests/fixtures/invalid/key/newline-4.stderr":"aa2b599e10e8a20bec1d1fe112ec90ccb5bed8d42e43dba8ccf0d5c18d4b131c","tests/fixtures/invalid/key/newline-5.stderr":"961866b075ab56f652c9661e900070b729ce5db2f57210e95f9c3955f5ba4b41","tests/fixtures/invalid/key/no-eol.stderr":"e8f14df57190fc8a0f4e704a38c42126aef0663f9315f84d23c371f0860a5a73","tests/fixtures/invalid/key/open-bracket.stderr":"330a1a96948b442437bd81f461e91e0b328e225abd304832b7d1660eebc77c5b","tests/fixtures/invalid/key/partial-quoted.stderr":"29dd53ef16e939b137da2c7d62c24aa2481bd44359a02578b77597296b25efc8","tests/fixtures/invalid/key/quoted-unclosed-1.stderr":"6b566ea67dd061ec05c6754bebdb583d93934412cdc6176bbd68d3bd403cca7a","tests/fixtures/invalid/key/quoted-unclosed-2.stderr":"396c4f9bd1ff398b439ae54b23452007e1e7518b38232b89b0f5b1b448e764cb","tests/fixtures/invalid/key/single-open-bracket.stderr":"52d8e13b39b9507ef37a65c0eb779e89ff7d00fd4a70245d44225a35200a6ae7","tests/fixtures/invalid/key/space.stderr":"9f3d6ed39f1a05ac99bedceb66c53835090182b47293bdfa0c080ac80285bbb0","tests/fixtures/invalid/key/special-character.stderr":"68aa496dfd623d18af16fac5b1fbf5f74365fe0c05feb3e5d34bcbe47fb1472e","tests/fixtures/invalid/key/start-bracket.stderr":"93a0e8ada8447d60ae738b4c0816d9087a84932dad2651505327ff37d3b12c86","tests/fixtures/invalid/key/start-dot.stderr":"25b23ad9a454746edcffc91b79eee11bdc9d9daf67640aa8d28594a98da3ddc1","tests/fixtures/invalid/key/two-equals-1.stderr":"f5398f176b3b1a3fac4cc7d68d1e595e2f1b60f92ebdd4f3f9bbe5d7191eef3e","tests/fixtures/invalid/key/two-equals-2.stderr":"099b5d50827299b921c3c1c00a2c804d580f67d06c2e5593660c6d6ad5900a66","tests/fixtures/invalid/key/two-equals-3.stderr":"b51c719bd0d8f585b4b017937e7d0df2ded58e3a37e2cc17cf9bf04277347a1b","tests/fixtures/invalid/key/without-value-1.stderr":"b1a087f97400f49a9bd6ba8bf44b3afbc3a0db6423d17b95fca5fc5be7a3a775","tests/fixtures/invalid/key/without-value-2.stderr":"cef84c2625e41ab3fda621f35d5e936a8d43bd292f3856f80cd2a148efc6d6b7","tests/fixtures/invalid/key/without-value-3.stderr":"295097c33c87dd153e607b3aaf648168678e6773e28ed0148d0b2e5144c1c0e7","tests/fixtures/invalid/key/without-value-4.stderr":"65173e2596ae1ac268c1f5a66fcb2e83be5663425aecf3fb3a99091b6fd40ea3","tests/fixtures/invalid/key/without-value-5.stderr":"9279c3cddcea9568abe57303cb5de41d8d1c71ac4ec991e1fd28651776314b57","tests/fixtures/invalid/key/without-value-6.stderr":"dfd20b35750e3a3cf1da7e8e1f20043235c0349cfd118bb12cf1a42165cc7ec5","tests/fixtures/invalid/key/without-value-7.stderr":"d0183883abf80ce86271bb0b8704637f93830cccb4c853f7834d132c9f153d4e","tests/fixtures/invalid/local-date/feb-29.stderr":"4c97e157ddaf9940d438560f34642ec4903609417905c02456ae386ade5573bc","tests/fixtures/invalid/local-date/feb-30.stderr":"1710668c24dd3aa9fde6e0f782eb880af872e4bb5a5186b281c8e6ed40e86dca","tests/fixtures/invalid/local-date/mday-over.stderr":"546af4611d7a589f4907bb085b74a5d74aab7505b613c448d8075aa9f14633d5","tests/fixtures/invalid/local-date/mday-under.stderr":"102c21e5b546a4a069c576d7ccc5a1bc259522e951fd7487e7ceb26f982bcc43","tests/fixtures/invalid/local-date/month-over.stderr":"4cac731d0f95392744498706c91b4940a30254d3910856da666bcdf1610c80fe","tests/fixtures/invalid/local-date/month-under.stderr":"df4ca9f526a23709fe9feb51b71b5ae3efac806a3fb337a40b626a27104b4124","tests/fixtures/invalid/local-date/no-leads-with-milli.stderr":"67e6de0c6025e5f498e0d5e0c7bdd2cd74a01cefe0a3992ad935b7b7f260fe93","tests/fixtures/invalid/local-date/no-leads.stderr":"d653403d2686bf1e56c21a120b7a6fdfd6e0f5bd231b14789259c23028e16dec","tests/fixtures/invalid/local-date/trailing-t.stderr":"9cf2e418427abe378484486356229b38f1db53842090a2d7d0637cad1a6b16c5","tests/fixtures/invalid/local-date/y10k.stderr":"ff9286e64bf70a2e35bed170d191349f0045fc8d6f184a183cbea693496e99e8","tests/fixtures/invalid/local-datetime/feb-29.stderr":"3fbdaad131492c5e7c85df1e90f73977a2f1236422b5fe86847fa000dbee7e5b","tests/fixtures/invalid/local-datetime/feb-30.stderr":"32e38bb349f6cde4e60534764f5839240313caeee12ddf4d97d4df404a6f3b01","tests/fixtures/invalid/local-datetime/hour-over.stderr":"0aefa752706aca82810109a94b9a5bbb8f50ebc4aa85bfb091466618c714c1f3","tests/fixtures/invalid/local-datetime/mday-over.stderr":"5873d8c67de9fd55e424aadcdb68d42842ccdee9f2e2e94e0bc42caea6635645","tests/fixtures/invalid/local-datetime/mday-under.stderr":"a1df2d77cf02f3cfe2a44daa946978aeff84db9ee033e61358e2610ef8acd3d3","tests/fixtures/invalid/local-datetime/minute-over.stderr":"1717bf680a89f59a66d5dae8dc98a17757b8973f70f869744ce6b496fa2c7c9d","tests/fixtures/invalid/local-datetime/month-over.stderr":"ffc05504137e79700f4c78955470e975bee840580cf10229202903a498e09858","tests/fixtures/invalid/local-datetime/month-under.stderr":"8e77bd4dad24e1d7e5c017702410cea56a085585e7d97425c55761b0246efde8","tests/fixtures/invalid/local-datetime/no-leads-with-milli.stderr":"88a35c260a65588d5ca0246c0f27f5e5e6fe8836777e5998c9f3c4bacca1c8f2","tests/fixtures/invalid/local-datetime/no-leads.stderr":"40795bab32c16aa63e609add7f809c7c22d2764d98700b3eb91c20e6eea156e8","tests/fixtures/invalid/local-datetime/no-secs.stderr":"f946633b31380b72df35d6a0e371f4eebca26332abac1c3d2841918461df30cd","tests/fixtures/invalid/local-datetime/no-t.stderr":"802e0bcf8a66b32fe9f2d0571808782bd37a6f1ff8c5e92368b5ad4c18d2218a","tests/fixtures/invalid/local-datetime/second-over.stderr":"7494350b21ac24f7f30c95f568412d7214a66dd9b04fbf454647f0972588a7fc","tests/fixtures/invalid/local-datetime/time-no-leads.stderr":"5117ac62dd754a203f2fd6e5163a2ce352776bee513219252cec77c0ed71bbbb","tests/fixtures/invalid/local-datetime/y10k.stderr":"66cdcfa2f4dbd5270936e531a1f3a9ac54a70dec246f3ef93b180911c201db1d","tests/fixtures/invalid/local-time/hour-over.stderr":"0f0507c0f3888330f1346e6a4c100c4bcbbc6549599cd39249fb59c7923b1801","tests/fixtures/invalid/local-time/minute-over.stderr":"052a762b6f62e21d59d145da3d242489990f99023681a4a645022af743a836be","tests/fixtures/invalid/local-time/no-secs.stderr":"db11e76aebb5c91f57869f1dd14ff7b0ab19b400eeb8b15936d0d82e4a8d0fe0","tests/fixtures/invalid/local-time/second-over.stderr":"18402b0e6cf205f466e3ec3d409daeeb77e355d41548f11bd3e3369240a282d8","tests/fixtures/invalid/local-time/time-no-leads-2.stderr":"2e446fcabdeeba0c118d1d49b89d7ad1271dd9eaf1ba109ee445c4f68ee96214","tests/fixtures/invalid/local-time/time-no-leads.stderr":"70094ae81f89871b0804839a5cea0f7591ab55b6c72c5d2867004cd4a0e0d86f","tests/fixtures/invalid/spec/inline-table-2-0.stderr":"41118ecf5033fdb7de4edaf1abf90e524bb50f8f3c44ef0a080be4ba1470b82d","tests/fixtures/invalid/spec/inline-table-3-0.stderr":"b7ea14eb8c6ba2cb126dc3947d00ce10a5403103a682e719ff51efebae6bb613","tests/fixtures/invalid/spec/key-value-pair-1.stderr":"d8b4149dc0ede93fe7c0d7d02a0df9d31fb2456b87ebeb532623bd6f825c2d23","tests/fixtures/invalid/spec/keys-2.stderr":"de2e84dc37ec53bc12ca585a9d2586404a920375bc201d7b2643fdcd537da691","tests/fixtures/invalid/spec/string-4-0.stderr":"c6dc77f81979a92cb5612b7f4b8e96d41bad9bf7a9a706bff5a05a6c65f76e86","tests/fixtures/invalid/spec/string-7-0.stderr":"3226561489c03d8fc983b0584d40f18629f46a425c5c7f34a4e984e164a4eb38","tests/fixtures/invalid/spec/table-9-0.stderr":"23b1d3b522e9fac974c4ea254df4798adf574be6dfc8ef1107438c502b3aca4e","tests/fixtures/invalid/spec/table-9-1.stderr":"f0164ed288eedfed3a6fecabf305a93050a6c3cc6a9397894633e4ac33b2c834","tests/fixtures/invalid/string/bad-byte-escape.stderr":"5807e3215b0def7410283291aeeb46514fa0a65d4326bff3669585e0222d5b2c","tests/fixtures/invalid/string/bad-concat.stderr":"176458d0d31d02a16e9856a80b1b9c4dc040b3ee3c83c8a678df9b1bd12c1e4b","tests/fixtures/invalid/string/bad-escape-1.stderr":"388e58e25fd5fc78b2465f1258c952f4850211b19e9fb65cc31a29774a19aabd","tests/fixtures/invalid/string/bad-escape-2.stderr":"51829779b7317fe753c54b53eb08fba669b16fc970fc29699a0a497aa95dc623","tests/fixtures/invalid/string/bad-escape-3.stderr":"8e5050c043061a991f6cf84be6b6e6ce468a35dd2ac50a3a4ce2ca1e1d5304e1","tests/fixtures/invalid/string/bad-hex-esc-1.stderr":"68446cc8fe418df4441e15118161a953cbeb22822d67c54b81c8f97aa48005f3","tests/fixtures/invalid/string/bad-hex-esc-2.stderr":"d7d979d0c5700548eae7a42c7e87253de0118b67a67ef6dbeb0598d574800099","tests/fixtures/invalid/string/bad-hex-esc-3.stderr":"fee92d2f9937e322423201b3099c7a246fabae85ccc23e990b602e1f5e181d9f","tests/fixtures/invalid/string/bad-hex-esc-4.stderr":"2c6f994aebcd9e644f469c84edb9d8ba6b6b094a949410935a590e8e6e6122ef","tests/fixtures/invalid/string/bad-hex-esc-5.stderr":"10f740ce3c36f4a8f7a1f8ab5c84e0fa5e4c3a88ebe534f79858781079139a96","tests/fixtures/invalid/string/bad-multiline.stderr":"a1ecdec206e87461bb4bb27ecd21a43da48d1d5bef6d8d746958d45219ce9dfd","tests/fixtures/invalid/string/bad-slash-escape.stderr":"7f3b917db5a8d87484f8b03680a2cd60288e549c5bca0c9e00c7e531d3e0405c","tests/fixtures/invalid/string/bad-uni-esc-1.stderr":"385be83541cc94367c9849619740c8d198b9c138f6f4916685cead90fabc3b9b","tests/fixtures/invalid/string/bad-uni-esc-2.stderr":"592fd536f48c1aa6c4c9b454c5cf5e1a7473860602ff2875430d4a7e676b6594","tests/fixtures/invalid/string/bad-uni-esc-3.stderr":"f6c284685af63ac02fee8e8ee641b03e90893a03001834625ca22482cae4d29a","tests/fixtures/invalid/string/bad-uni-esc-4.stderr":"1df7029bc305307943cc58417f98cca4c854deba8475b305675b2505d4d9b83f","tests/fixtures/invalid/string/bad-uni-esc-5.stderr":"6cfc8fee06b9770271b1241ba6025a470b1ca8cbb80a3dce7d6ee1b2c2f7f637","tests/fixtures/invalid/string/bad-uni-esc-6.stderr":"73d8f5132741c15286327111496d475807dcb638b34c10db20bb37d96910c209","tests/fixtures/invalid/string/bad-uni-esc-7.stderr":"c9c967322b827675040ad78cf4fa1c9890bb13a124a2274eb693622d5ec50420","tests/fixtures/invalid/string/basic-byte-escapes.stderr":"50d06255616038364b52a0a32113d96e8b87de8e5ffcef93c2c88d0af9b57670","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-1.stderr":"b26e80921025c1fb0dfe33c01460e5ac96791585ab8204db3e76c83ccd404afb","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-2.stderr":"c9f8c422be49fb11111623c4322c421b25bb9592fade95bc1cc2ed9c3c2bd93d","tests/fixtures/invalid/string/basic-multiline-quotes.stderr":"70de515fa0500fe6ba3b915d224653824ae4e213cf41d9b3465b536a0007c623","tests/fixtures/invalid/string/basic-multiline-unknown-escape.stderr":"2c104ecdfe88a89893d167ad41167108208d3aeb0c22c7d5193950ac2805af60","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-1.stderr":"82b7d85f48c58289c81d149814c591195f361e23875dc2a9767215bf077a148c","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-2.stderr":"9b28ddf18556e9adaf77319877189112aa8ac93464ecc09a8c5020c3c13b143a","tests/fixtures/invalid/string/basic-unknown-escape.stderr":"81ebb5efff40faa0b5161e4d92faac81aaf85c038987ca3d0ce7ff28f91f3cd5","tests/fixtures/invalid/string/literal-multiline-quotes-1.stderr":"6776f505f0a2a6a67e3e5fbfa6ca0b9ca2653a1a623d1fb3b463305ec872699f","tests/fixtures/invalid/string/literal-multiline-quotes-2.stderr":"e304eb9ed3f7a136c74a1c70fe73f5f75a169ea93aba2789e6441f84fbcfb03c","tests/fixtures/invalid/string/missing-quotes.stderr":"ae9dea4e735224472bfcdb61471def4877404a0a3680e41340b0495552ea8133","tests/fixtures/invalid/string/multiline-bad-escape-1.stderr":"7c5fc3bf00f17344781150281839f78228905d85e41895f4c6d5fb67c7ef0a2c","tests/fixtures/invalid/string/multiline-bad-escape-2.stderr":"c4f5464eced80eb93368ee62ec14c6c4c8160ec7e33000b5d7840f748306b2ea","tests/fixtures/invalid/string/multiline-bad-escape-3.stderr":"ad14d3d916a15353394638613d6d79da2e004e7e522c48edbd2ff45edbcb2685","tests/fixtures/invalid/string/multiline-bad-escape-4.stderr":"e0b254f4a49cde181f4d8d94e032299e1506a09e8bc5bd54590a04251d5978ab","tests/fixtures/invalid/string/multiline-escape-space-1.stderr":"addc40c648a77da6e8ee3d63e73e58331bc5f8cff6d4ba67bfe679db464de835","tests/fixtures/invalid/string/multiline-escape-space-2.stderr":"8e9cc938972808cdeb4e8d2f180e1ff856000eb52f3e77ed5149658c15296d75","tests/fixtures/invalid/string/multiline-lit-no-close-1.stderr":"5e57a18bec64d8ca2fd53473aff6f1cdc8e5c35209ecf84a9e9da8ef3865fcfb","tests/fixtures/invalid/string/multiline-lit-no-close-2.stderr":"40a6c9ece9e2b03c137283b825b6c0380e5778e1c76d20ed2f46647e3ea6b25d","tests/fixtures/invalid/string/multiline-lit-no-close-3.stderr":"cfefa000b457e225de66a0618b2d245c0887099cda67c6ced68612df58a50c49","tests/fixtures/invalid/string/multiline-lit-no-close-4.stderr":"12629cc0445ac282832bebf35e0bb2eb1e305a4a4c147f4103773501d56d3bae","tests/fixtures/invalid/string/multiline-no-close-1.stderr":"40f5d85746b8d297b1cc27bfcb95b8bd2a2a471f0b820bca0b3437c4ff6fecc2","tests/fixtures/invalid/string/multiline-no-close-2.stderr":"3c121acbcad1ef6821da5452f0e9223f75915be50ab11d69efa498cd2c9685a4","tests/fixtures/invalid/string/multiline-no-close-3.stderr":"9850d68e589fb8ebe7b1cbf9f27e4ac991d27b939a5be577df02aa9dae58207d","tests/fixtures/invalid/string/multiline-no-close-4.stderr":"dd0703ac4d584850805bb5a7afdb1e68db06fef1dbe2a93ba85b01ec76da6b9a","tests/fixtures/invalid/string/multiline-no-close-5.stderr":"c6874fe259394f8a14636182b4fb0f20009d678853fbf4f5c7e0e0d53fe0b789","tests/fixtures/invalid/string/multiline-quotes-1.stderr":"4d524fc6b34800ef6093894044763f1eba2b4fffc110b2558ad4f76249985651","tests/fixtures/invalid/string/no-close-1.stderr":"ee585c98fe640bd5a6edb8c5930358b1a7c9085d07e7fb318bc094e81dc6e1a5","tests/fixtures/invalid/string/no-close-2.stderr":"2284df23ebcab2f4a535e31726035f5cdda9d56190269a8189bd959fb806c636","tests/fixtures/invalid/string/no-close-3.stderr":"30cb3d9d517219b70c7d468207f0f4567d80b00330a943f482803980d8f65998","tests/fixtures/invalid/string/no-close-4.stderr":"4ff50513bc0249a3e503db5f255c1251bfdcdb764a6541d484f7e09bf4efbcab","tests/fixtures/invalid/string/string.stderr":"68446cc8fe418df4441e15118161a953cbeb22822d67c54b81c8f97aa48005f3","tests/fixtures/invalid/string/text-after-string.stderr":"a20980f6482cb204272c705c89ee6cc0987b44c5fb357466f05fe442754c1cc7","tests/fixtures/invalid/string/wrong-close.stderr":"6b376b179dd7022bea271e63e77f0afc15076cd4ad0c81bae6007fdc430072a4","tests/fixtures/invalid/table/append-to-array-with-dotted-keys.stderr":"76b544968f3a5771cd2f5efad26167ab5bdcbd8b067586ec0e602281ae5869dd","tests/fixtures/invalid/table/append-with-dotted-keys-1.stderr":"1379eea8bb3f62ddc212705a525cad262da23f16b03bd0626650c61c6811a680","tests/fixtures/invalid/table/append-with-dotted-keys-2.stderr":"7b0ce2aee2d44c234a14bab3fb339661f00b3e3bd39784f3ae7058f3a48fbdc7","tests/fixtures/invalid/table/array-empty.stderr":"e3cd186fd3a22b55fb7e18c6247f91c8da4b61396bde01ab970847aba933b80d","tests/fixtures/invalid/table/array-implicit.stderr":"dcff7e1489ff2a416a5945891c7007152ebbb8cd028baff4439ad2167a1f5976","tests/fixtures/invalid/table/array-no-close-1.stderr":"a816d34d85c8a0ced8cb9315723ea1483711a6f19f6e2e2c96a409d7d43c6fe0","tests/fixtures/invalid/table/array-no-close-2.stderr":"b07a23ed41ea508abc35fcaaad1b38d30705ad874e62ecfb6affbffb39ff4487","tests/fixtures/invalid/table/duplicate-key-dotted-array.stderr":"abfbf76b0555af439f38404ff1f4b2736a759f1bfca347f5eeb8a3754312e835","tests/fixtures/invalid/table/duplicate-key-dotted-table.stderr":"a55218213d972554ab52867926d6c108a308c009b8be13e05578cba1a8638f67","tests/fixtures/invalid/table/duplicate-key-dotted-table2.stderr":"ebabea3cc176fc9f85083f697fd9d6ef97d7230e369be4c504e78c00a82672de","tests/fixtures/invalid/table/duplicate-key-table.stderr":"58598e44d8ab8e8fcaa0f9ddbe2415013ade6965db761d99162312cde1e56fd8","tests/fixtures/invalid/table/duplicate-table-array.stderr":"4079ad8885e7fdd2a0674c31767efb16350ac5906ea5f71f980e06b804a515ba","tests/fixtures/invalid/table/duplicate-table-array2.stderr":"9b647eef2e77b8538ea32d0533b80c870fa4173f0a5b151399c84d7edde3f31a","tests/fixtures/invalid/table/duplicate.stderr":"f537ae526887a2288b943ec1b2d3ee31bf0c9193feaa27bb9e8c447e104ece6d","tests/fixtures/invalid/table/empty-implicit-table.stderr":"b331fd776de53639f8b6f9740f0922ca97aa3919ea365904829dbd61e39a58d4","tests/fixtures/invalid/table/empty.stderr":"bc6c1109c2dea2c69becd952c68f7ced16d983051a9a0b32af1868349e4f2f40","tests/fixtures/invalid/table/equals-sign.stderr":"93ab20fbe68fd87648a4ee58d8af26ea7224818f605fa4cdb2ec5aaad652d050","tests/fixtures/invalid/table/llbrace.stderr":"90b78fd64904ea772687d8fea6df07b684a6d278a47aab242eeace8547921a18","tests/fixtures/invalid/table/nested-brackets-close.stderr":"85435c519a813544af5e686cd89f0a7c2944ab30806ee6fc3632266a03801a51","tests/fixtures/invalid/table/nested-brackets-open.stderr":"2bbca8c47edf49cb4541d869ec198beb5167bd9ea1e40d7daa461f03c5554293","tests/fixtures/invalid/table/no-close-1.stderr":"6e1bbe0ba02c31cd687a9dc8fe0fde2ce1c0cda31aec651dff58f41d8aa7d62a","tests/fixtures/invalid/table/no-close-2.stderr":"6e50b27f819a6769accee8b45850a2181932a8809fcdaf347c6d129ae6da307c","tests/fixtures/invalid/table/no-close-3.stderr":"68ce4165d0133af527af7c56fd1af4e652af27cc221f1051a8073e7192a78a20","tests/fixtures/invalid/table/no-close-4.stderr":"52d8e13b39b9507ef37a65c0eb779e89ff7d00fd4a70245d44225a35200a6ae7","tests/fixtures/invalid/table/no-close-5.stderr":"7c3f3c078dbeffcf3b55d3ba811d89fbdd19c1d2ae62b290224003ad26863a00","tests/fixtures/invalid/table/overwrite-array-in-parent.stderr":"f4f94892b9268181ad4e5d64337ec3bd11d0b1a49add7a7c52a0a949f511e79e","tests/fixtures/invalid/table/overwrite-bool-with-array.stderr":"1773dc739ded58c66dadc101668fde5877ba018268dc3b124a4b788b0a0f1c68","tests/fixtures/invalid/table/overwrite-with-deep-table.stderr":"7e1c743c45e4919cab3d36cff96a2165f15cd749411d8c9e758c7ccb7d972893","tests/fixtures/invalid/table/redefine-1.stderr":"a8795ab0ce678bea6299751633e08747b862a46c8f4546094cd1e5f643715202","tests/fixtures/invalid/table/redefine-2.stderr":"dbd674695c75ab83513cf0207304e7ec84565d6dac607e8da85eb9cf41475806","tests/fixtures/invalid/table/redefine-3.stderr":"e161dd3883e7d38e558d296898b637a09ff287cf35a8d921577693fd3c7a2770","tests/fixtures/invalid/table/rrbrace.stderr":"412d28cdee519340465a0e764529acffd5947f566d8b87d69a1a9890845e3747","tests/fixtures/invalid/table/super-twice.stderr":"c9d37817b5e5afd36a2ef3f12190ccf512217e5fdbb78d075d873ca2d2a90f98","tests/fixtures/invalid/table/text-after-table.stderr":"accc6467f519ab738eaa646c255d527ae47c4958231db59b2a9ef16549dc2fbe","tests/fixtures/invalid/table/whitespace.stderr":"73d125e308a219ce5aba0d45ea17b6247eda746e76740f640066b891092efd16","tests/fixtures/invalid/table/with-pound.stderr":"eb3fd32c944de502852f2e7b453f3f56849e7fbdbe9060863f9d65e308bd7f2d","tests/invalid.rs":"a8ca04aa4970cd310aaa01b97a55f78f84e6ccc0cae0974bb5e3b78056f5f437","tests/testsuite/convert.rs":"bb224d372b98c2c746c0ed127dd4c0289b1486b070b5d126939a412add34b461","tests/testsuite/datetime.rs":"62354b8e44466ecb63e0b64ab5e70212075cc42bc5ddf00d513004968f01c15e","tests/testsuite/edit.rs":"ea917588c1e2a331864f59340d4c385fc20156d551ead34438908d76522ed434","tests/testsuite/float.rs":"91b0be9a56c5b0ce7e54f7eda3ec72efbb33f855ebd49da660483bba3935586a","tests/testsuite/invalid.rs":"b2998cd7a30c8dc96f614e938edc9a7bf2eccf5662706994241294220319319a","tests/testsuite/main.rs":"c85d415a8c20f47f383c051fe7cfe3324947844bee922d6135cabdf63ada8403","tests/testsuite/parse.rs":"e3c19bc40167768cd50613ffef316c1ea31bdba3d9f52e997a0a84cc67d2ec19","tests/testsuite/stackoverflow.rs":"74df28497f99d467cb710eb5b3c48e77360747c7c21242e6a82c55389f0d92e1"}}
\ No newline at end of file
diff --git a/crates/toml_edit/.cargo-checksum.json b/crates/toml_edit/.cargo-checksum.json
index 03d4a27..3875464 100644
--- a/crates/toml_edit/.cargo-checksum.json
+++ b/crates/toml_edit/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.lock":"62838a7b98d7aad49051d982c6e4afc306a1313189d8bdc751386e0d10addfa2","Cargo.toml":"7f220fca5b096b55d92d569c4f835a863f2daa422bedf85e800fd362e80c3d81","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"39f3575e0849928f6f8fa4cfff6f44c96fff96dc1146d761830a87a6ad33cdbc","examples/visit.rs":"657756caba28aa87a8b10104aba704fd2365579c57a293bc572a8d72173ba174","src/array.rs":"0f0bf16d16a4af2257ff8c93f1e1ef4684509182c57b4f90dfbc67554e00d737","src/array_of_tables.rs":"7b87bfb27be3fcec86cbfbe974c65269b2a842f5cd54c6a842c9024db4aa229e","src/de/array.rs":"7969d28e98e366cd16c9ed74128404a147a2b787c54d0450594811a92d17b310","src/de/datetime.rs":"a29e76372fc382686c31ee93fd54c749ea3d775c8d4fcc2b2643f9464ea23730","src/de/key.rs":"0b4c60586421a0c7b64ce93d41717823d93aadb99ff360169f7e68ded8eee9e2","src/de/mod.rs":"6bcebc228026917a5fc51c497c0751707bc745c7be9919379ff2a1fbe9d311f8","src/de/spanned.rs":"5c959639bf4b3d19c6b18e625a57b28b311164bf7b191bbc4d30aa97b7239679","src/de/table.rs":"a56756925b474e9c9528b923be02b5ddd4a3193c3693dfd4d71dd4544732bb9c","src/de/table_enum.rs":"ad3aab5169d6ddb2e7cd50a4dced4ee2485b11388876474969137b12fe6ee1e0","src/de/value.rs":"ed366fde8ec410e0f0a9ffd6a7ffa53c932572a0d258f8d28c63917714bd1db9","src/document.rs":"d436cc039f2fd954f476d2fbc8860f3f8247a62caa11f2055346f23e30c277fb","src/encode.rs":"83c966f5818f7f927a6079c704713bc1cfb24d6e18a44190ee807ac6b4c5bdac","src/error.rs":"90beabbd868c03d1c79afb1ea93c0d5478840e0ba1f13d10216d043fd0ba9760","src/index.rs":"34ed0a4cc0cd55ce29885ed8123cc7c09c8ba3b9aa8d3b0b47554c1a41d31577","src/inline_table.rs":"07b7219a72ac929edd66f6126151fc6f43929dfce167333fa28344cc12f49245","src/internal_string.rs":"8c84f94363ed012eb7114c6b3f6b371155d0ce740ce44ec9d315aedf048c1d11","src/item.rs":"ff66fb037fa7cf1d055777fda19ec4febf6c45daf30035d7f10777e49e01a817","src/key.rs":"de34ad87cf7014ed9ecfdcaae6b20c2ea739c362926ec5e3bbc2facc567dda22","src/lib.rs":"ef1f214e1e7b6277b33ad3bd3b180dd33f8582c8f8fc91dc6bfb5ab80c66b690","src/parser/array.rs":"277cf3ca33688cb484843e2df9096e1fe076e6ee7e9132798284ce2a6d641475","src/parser/datetime.rs":"84d4d3c147466c9cfa21d40c64de25fb8bc5fbbd643f40f4a7ac50483f5a8c3e","src/parser/document.rs":"0b94e7e0bd53606949d66bb6854bf275bed399b869bbce18d6acbd65238de112","src/parser/error.rs":"025bce90fab50af89eaaa161f9116a577604632858a681cf8167904ddbfe354f","src/parser/inline_table.rs":"adc95b2c77ad256a56800deead8ef06002db6dbba60755563e445a344e1a836e","src/parser/key.rs":"69191a18ddf67a77f6e09a593ad03393cea642324124a32c0109debb16849992","src/parser/mod.rs":"a4bd31baa663a3ed5f43d210fe566941b9b42d86b7eb57f4138ae03a6ca3a3bf","src/parser/numbers.rs":"cdf43253a8e88260373a41dd5e89e0f693952e5aef010ea412d9daa5eb4d4f91","src/parser/state.rs":"20d0ff7aeea8eef888cd3a3b1aef47616c55dff77bb61d5258c493ca58351f33","src/parser/strings.rs":"82d73152c536b6254f7698c70c0bc29ef1b4c29877e8b86295ec88bd28fd31a3","src/parser/table.rs":"c716d82caed27229afb22e757cb14b2828356fc69d33ab94d8f43043f992cd2b","src/parser/trivia.rs":"f93ad3217f5cc5876ae9b4aa20b64d13bb3dc2e39fa941315f0cfaefb6c3efa3","src/parser/value.rs":"f221bc3c6479d6243bfda0b5a8ba545944d93fa86170cab2dd4319c75677ad01","src/raw_string.rs":"f26542a3a7325b29fe6b72c8facb865ebce42b270d2126dd1aad06b5ef0c57ae","src/repr.rs":"d259af1a8ab17312945d913be2a46e0701cc2a367cfc4e6bb7c0a55d8ef5f989","src/ser/array.rs":"5fb55303365bf6991f7af523931d1a046e88d3125c099317e12a7d4f592321a8","src/ser/key.rs":"9bad3bee90876c5c54aaa5caee92bfe30d79a100615902118b35b3ceb40b524e","src/ser/map.rs":"148423fba5b370335f832b2f6851adae29f4cf90dd05ace562e44cf2bf071aa2","src/ser/mod.rs":"49471b5e6e82fd95c9f87549e2c0f0f3105bc7c1c697ceb7481deb43ae96f31c","src/ser/pretty.rs":"9a9f6d43b2eab487de8af216b9950c93318c9ee6a91f4707ffa41e2ee492c902","src/ser/value.rs":"3bd14fc54ea343cbc04a5b74461aab0eb1043b558cbc58bd02a7d08bbc020ee2","src/table.rs":"614c3f669b3230f7a34b1768cd422b052f2bf17784dff0fdae66757b9d007fba","src/value.rs":"9a40f0931103665830713adfc8b39aa1aa3b5030220b86fb9d42080215e5e133","src/visit.rs":"b1dd699301d93c2a01558f44712b831bcdbd665066a7e0816590f82edae04a46","src/visit_mut.rs":"10d3de01225fe1181dc3c19992069e495299ee63514ace5f4ab058df8855bbf4","tests/decoder.rs":"8093389d542f50974d6a606321b0413941709ac2cbdef48934b60088bb21e3a3","tests/decoder_compliance.rs":"d804ef45b0498869d05ac5c1216dc29e94963b2ec4f814e867f0fe8b71e9c639","tests/encoder.rs":"c84dd8e14ade77a4d55f932418567196e188fe65d3957408b9ce07c2c27c4da0","tests/encoder_compliance.rs":"ca696031cb8f664728521da0370eb78e0351a5ac7134ad6cb12bc25f85757c97","tests/fixtures/invalid/array/array.stderr":"db4f1f06909589f9cb49d6657b6c39444e53b4ec8c4d3f0b9704ab5beaf68d2a","tests/fixtures/invalid/array/double-comma-1.stderr":"db4f1f06909589f9cb49d6657b6c39444e53b4ec8c4d3f0b9704ab5beaf68d2a","tests/fixtures/invalid/array/double-comma-2.stderr":"da4b8df6dbc2ef41b7b1b08b65a160ba37d07dec0c1ccf4184d6381a857b8762","tests/fixtures/invalid/array/extend-defined-aot.stderr":"a9dec506541bbdb96725d9f989572c10f29a52eb4e167fadea8929d7b6729b7b","tests/fixtures/invalid/array/extending-table.stderr":"a6c077cb49c41a9d10aca0bac6c571e570f0a772c6311d6a835f9cc46f4ab7cd","tests/fixtures/invalid/array/missing-separator-1.stderr":"98773b4d82cc32c0f8d1d8e6078109f7d9655b13d58e4b62fd22cef9f9ad725c","tests/fixtures/invalid/array/missing-separator-2.stderr":"eb38b2683f119d8b9b8c6b8ffd46da784112565ef1033febbe59469e14baea76","tests/fixtures/invalid/array/no-close-1.stderr":"a02ff4b3edf627873f0cb1f4242305f6a9a032b6f897fa13b8070c1a9ae85afe","tests/fixtures/invalid/array/no-close-2.stderr":"dfac1f12f502214388deecedf7a9720338ffd13b3184178410581e242ba8124b","tests/fixtures/invalid/array/no-close-3.stderr":"1930ad00952820805e208ebaf1ae3df243f492b6b2924bbfffbe458f31d89b29","tests/fixtures/invalid/array/no-close-4.stderr":"198293e991ab522435e383ef906f0594a0ea8126c792db934eba142369189e1c","tests/fixtures/invalid/array/no-close-5.stderr":"279aa5db5d92d3e729fe326321d2d217ff33db3c5e2bc2a5043909fc798c226e","tests/fixtures/invalid/array/no-close-6.stderr":"ccd74b7a7c01bb96a748c0e797f6207f0314d76520e4c1d1a9cde076e2284216","tests/fixtures/invalid/array/no-close-7.stderr":"9c6ab0c93fcf0c9ff7fdf8bc13f3b62864305509b80659c225a55641133abcf5","tests/fixtures/invalid/array/no-close-8.stderr":"c4b7fe79f9a354152c36e130af4862c47908cff3c84ba9be8bdbe76c35d9c6af","tests/fixtures/invalid/array/no-close-table-1.stderr":"545ad4381d5a007a6cd940a4523ae2f629617d298611c0898283a1c4783604cb","tests/fixtures/invalid/array/no-close-table-2.stderr":"5a2b4bf9026aa20c749551cd77458c5ffba008b5b235fa05fb7c756917018eb8","tests/fixtures/invalid/array/no-comma-1.stderr":"7d2f2923638cecc2f89e8ff1334a9da44fa7e137bea12d5019ef25f576f015af","tests/fixtures/invalid/array/no-comma-2.stderr":"d95a7a43534c632efa2502e3b5f8eedabf2f05ad6bfdd68e2f9fd3d8a22ba028","tests/fixtures/invalid/array/no-comma-3.stderr":"ae8ec653f564b247644854fecf59e097cc32e445aa4775aed8d18a207af5ec45","tests/fixtures/invalid/array/only-comma-1.stderr":"6f35677e711812007adc9a59341bc62bf2b7bcec80a456d19a2468ea8fd27b47","tests/fixtures/invalid/array/only-comma-2.stderr":"9d0017e6798574bd85810e64816ddfd08ccc36e25153006958fe023e2a007331","tests/fixtures/invalid/array/tables-1.stderr":"f105a34c2d87b61160881eeb09b7f54d244ba2a222d32fbfc755091939942247","tests/fixtures/invalid/array/tables-2.stderr":"77010599d1d61a34119a99acea7d84162d217df93bca01aed3ae73f1eb62dafe","tests/fixtures/invalid/array/text-after-array-entries.stderr":"391ee42f4fa3a7ec51ba1b90e69f1d9278c105426fe66ae1f80e65d7fb6ed379","tests/fixtures/invalid/array/text-before-array-separator.stderr":"7292ebcb8c9c8aaa4041279af5414de3e710977cac948988cdc8b0947223b62b","tests/fixtures/invalid/array/text-in-array.stderr":"0486e3ec5d299e39c61380a2ed8826d886edb730f6d9555a765e4314da7f5b68","tests/fixtures/invalid/bool/almost-false-with-extra.stderr":"d489307ba1d0a3dcd1bcff067437f0cde1521c6fddb1311bf396468521eabe81","tests/fixtures/invalid/bool/almost-false.stderr":"399829efe867bedbcef2ad2073df621f8297877180f71c6292209426da7b09dc","tests/fixtures/invalid/bool/almost-true-with-extra.stderr":"439c66bbdcdd335cb858f3cfc692fa3a78f7802591954909fb6807a62440d334","tests/fixtures/invalid/bool/almost-true.stderr":"3aed4e8704abc2099d8a881ee496a868c4745a0cfe7df781de613016543f4313","tests/fixtures/invalid/bool/bool.stderr":"d489307ba1d0a3dcd1bcff067437f0cde1521c6fddb1311bf396468521eabe81","tests/fixtures/invalid/bool/capitalized-false.stderr":"09c2b3b51460a52bc274c5d0b08024c76ea3dd7e067ef20765cc56d2a4f60087","tests/fixtures/invalid/bool/capitalized-true.stderr":"d0fa94dc8a4fb0f17550052fde5e10134e8ccbc84f1e90093446f1cb99f01a70","tests/fixtures/invalid/bool/just-f.stderr":"5f6a3a206bf49fb5eade446dc8f432037a4026f49beac01e453fd8c3610901b2","tests/fixtures/invalid/bool/just-t.stderr":"175bdb4ffa7259c5bc346fb088de8545e8e1d8254d1e2ac0d33c16b539e2270e","tests/fixtures/invalid/bool/mixed-case-false.stderr":"9de4d4b695614f3593abcb27a6f68ef5c578a96bec01472607c35cde28d48b10","tests/fixtures/invalid/bool/mixed-case-true.stderr":"45e314b026b819d568350bb62a39a2e5fd970acaf0a6591539d043abb9dee629","tests/fixtures/invalid/bool/mixed-case.stderr":"927b85ba3c3084e925cb8959c07d42f9f19f4ca8f84d7dd6babf15f1d9a1048f","tests/fixtures/invalid/bool/starting-same-false.stderr":"7e0f1f2d8ae538a5b156da19be0630d25e0d2dfa44bd97cdb125bf3e042df7d3","tests/fixtures/invalid/bool/starting-same-true.stderr":"bdf30c50dcc0ff8c928e753962f6dba7acc6f945a2958e0b03e690a126270ea0","tests/fixtures/invalid/bool/wrong-case-false.stderr":"811f36bc4df7755c3850b4e933e5436fb54183ac7fcf3599e8373d7545be1c2c","tests/fixtures/invalid/bool/wrong-case-true.stderr":"90520b03257419ec4a826b7f989e5aa74314a0aeeceaf6bf1873e867715495a6","tests/fixtures/invalid/control/bare-cr.stderr":"568eff61d56bb362866973d9a7a488831b8b08d02f4808538a04b22ebe10bf09","tests/fixtures/invalid/control/bare-formfeed.stderr":"86ebf66dcdf202fd832d91892b4b62eca65957198cca9567b10f3400997e9f90","tests/fixtures/invalid/control/bare-null.stderr":"e6bf2df7229c8632a1c7bce682914166520216c2d6c3ccd4c2e716dc700d5bca","tests/fixtures/invalid/control/bare-vertical-tab.stderr":"b85c55e64c58b85343482dadcd5d833fa771a79e1d0f13a24f4185a4d0d826d5","tests/fixtures/invalid/control/comment-cr.stderr":"3b334037fc70762903ebc2c3d0786554bb62d9f146dc801e07841ac7f8370e78","tests/fixtures/invalid/control/comment-del.stderr":"872065efcbcddffab0679160bbce0c8e2519ae7ccb8b4b667a8b78c4bd13522d","tests/fixtures/invalid/control/comment-ff.stderr":"bdcaf413adee168eaee3a7723b2939fe67e59914a834cbc559902eb94e4ca02b","tests/fixtures/invalid/control/comment-lf.stderr":"898787080f260e05ca20e6982ac4cce5572a6ed84a9244ce07a41f710db9a0cf","tests/fixtures/invalid/control/comment-null.stderr":"bcc48727ae370e45918682d6a17bc680b126d4792d4a33524c802f45cebc03d6","tests/fixtures/invalid/control/comment-us.stderr":"37d7a6c0a28989966af74530c2885bfc7ba6ddb31b58bce3f26543e34043b88c","tests/fixtures/invalid/control/control.stderr":"6ba75c9dbd0e03531f5a5ead2cb781a702d3600c76b2a4cf1bf7c02c1c9aca1a","tests/fixtures/invalid/control/multi-cr.stderr":"29fde9a540e77ed46dae0a227b666a9c102d5263cc5cac811e0e451bd403ad91","tests/fixtures/invalid/control/multi-del.stderr":"79f0d85f5b44a7dcad9d98adbef25b6ce54bb6dbf79ffcd3ea230a07144b4b82","tests/fixtures/invalid/control/multi-lf.stderr":"8c95b2a7b4e20dd8985e04b8da5fb9d7cbff37220a74fd3903f16f7ea7eaf39d","tests/fixtures/invalid/control/multi-null.stderr":"9fec8ad3ba45ddb96ad3b6a118b4fa648056e26a9128528b2c0a8fa3b01e741c","tests/fixtures/invalid/control/multi-us.stderr":"3a59d615cb91172936acdc42baa39e9faf3a2bb9e9078d9879a54826ffb6b20c","tests/fixtures/invalid/control/rawmulti-cd.stderr":"a71dce0ac9a79209ea173f3d656b2d663c685b47841b620cc89860fb68f0cf0b","tests/fixtures/invalid/control/rawmulti-del.stderr":"3b99e52cbec29c7b6b439bcf3cd58b85a72487e2f5af5e829dd6986b3414f49f","tests/fixtures/invalid/control/rawmulti-lf.stderr":"89fb9c7d02e39ff648cea46ec1086058016c5ef1deebc6f3b106664dc4d10eae","tests/fixtures/invalid/control/rawmulti-null.stderr":"84c04cc89a6bc716b6f7811142899014abdb0b49c4ea56bc163c19220b14c323","tests/fixtures/invalid/control/rawmulti-us.stderr":"ac53d1dcc96c3beb454f5474a23940f26c93705b76a10647c810f79facf4f6be","tests/fixtures/invalid/control/rawstring-cr.stderr":"fa8da523c3cc24384fda1dd6032607cdd380f45133ce7d6d2a1d3b8a3eb6a917","tests/fixtures/invalid/control/rawstring-del.stderr":"64378c4341fb92376e2fe8a70356411759b8659e151ed0ca744751c8b2d32155","tests/fixtures/invalid/control/rawstring-lf.stderr":"43f6431efef7ead5aa335958187d979d98dcced50cb82ccca19df34397278175","tests/fixtures/invalid/control/rawstring-null.stderr":"eafa2a63e9d12293b290405049457860a8fef70de56c4ba2f203e5f2c79a8634","tests/fixtures/invalid/control/rawstring-us.stderr":"d139a7ec7c4ff5358f6c56ea2f2e431646f5ae9bf3d927694a2aa3891637ecb0","tests/fixtures/invalid/control/string-bs.stderr":"ce6634df580f80e090749d31c178bed74b88718befea7788abe801acf4af10ae","tests/fixtures/invalid/control/string-cr.stderr":"a8dfbe00e976920a442f8f03bebeb31bbdb570a242e380b5a4c60d351614acf9","tests/fixtures/invalid/control/string-del.stderr":"bd862371b7a698647d4d68e5c3bad5c269bd8553f36d82301ffc62a4508334a4","tests/fixtures/invalid/control/string-lf.stderr":"18a1d0e1bcf6f99c80e95e94097f95722a65c2d8a415f9497b808b0bc135c12d","tests/fixtures/invalid/control/string-null.stderr":"fe34d284c478853bad94235aac1f37ac3c591f97e12990847b5da0b6c99bd47d","tests/fixtures/invalid/control/string-us.stderr":"06f7fce918057f567803f370e55e035896bb63a97eb0c0c39a680d6b927fead8","tests/fixtures/invalid/datetime/feb-29.stderr":"898cd0c4cd094247fa0825d02179d3b913cc9db4fbe634fa84c7a7d2cfb7b03e","tests/fixtures/invalid/datetime/feb-30.stderr":"0fb2c5f14857fe5edadbc93a78208c1c9ed900a18e3296613cc620e255c76e11","tests/fixtures/invalid/datetime/hour-over.stderr":"bd2220bdbaa96caf3d2aaff640620e4856bffb722a0e5be61dcb5283ffd08056","tests/fixtures/invalid/datetime/mday-over.stderr":"de9d27d65c68dd09da10c229167ce881dfe0ebda457badfe24b7598ae80c47a6","tests/fixtures/invalid/datetime/mday-under.stderr":"18daf3ce2a6a972476ccabcf92690a488e4f3be804dab8458da2aebad22a2c8c","tests/fixtures/invalid/datetime/minute-over.stderr":"a29009d3f7a6b1d9afad2420f223d6a6e02df8149577547837f5eeec4075bb9a","tests/fixtures/invalid/datetime/month-over.stderr":"37a203b22c3b2510541e413ff347447f9f3319a896ee005b96f65bc0d68150f4","tests/fixtures/invalid/datetime/month-under.stderr":"24c554595ca9a999a1d8e1ef7dc28b443f2f0ad6e17337ee157fb18bdcf678c1","tests/fixtures/invalid/datetime/no-leads-month.stderr":"2e20cb60a1ecee85b172d1402e4d8c425362e4db607706bd39494385dc6dc98e","tests/fixtures/invalid/datetime/no-leads-with-milli.stderr":"a35c496884e921aa086c1404bc812ff74f2bfd347a3ecd96640942be5555afbb","tests/fixtures/invalid/datetime/no-leads.stderr":"2e20cb60a1ecee85b172d1402e4d8c425362e4db607706bd39494385dc6dc98e","tests/fixtures/invalid/datetime/no-secs.stderr":"65871ee020e645e737c363b22cf43c160b295871cd4ac97a37d3ea46f60e3250","tests/fixtures/invalid/datetime/no-t.stderr":"ff50b85f6bc0d49000ec6f1303fda9b44bf934c2ede61743363411bbf6ebecbb","tests/fixtures/invalid/datetime/second-over.stderr":"0ed555a874efa08b711b5227501208758d87a01ad8360cf76c3dc8761807fac4","tests/fixtures/invalid/datetime/time-no-leads.stderr":"b3282cb32386dd84a35468f488be5a92dd3488e951f9dd2ea39057046386b73e","tests/fixtures/invalid/datetime/y10k.stderr":"6fbe45e2032a4b8a90fef144618ef6027edc00dee219d50c8b1493b6e38586a4","tests/fixtures/invalid/encoding/bad-codepoint.stderr":"1a816a8cdd5c2c9b8ae10431d981e22c3b307e30ef3d401ab62ac1012240be44","tests/fixtures/invalid/encoding/bad-utf8-at-end.stderr":"518dc443f0404d486b40bbbd152870276016795b05f3cc8a1de64a0e08fcdda2","tests/fixtures/invalid/encoding/bad-utf8-in-comment.stderr":"e0f252d14c18ea072c098834997db8e5f68b807bb0fa6d3d34e4042a5ea6fbb7","tests/fixtures/invalid/encoding/bad-utf8-in-multiline-literal.stderr":"2328a89cd9043de10ee656f4ea0dd5e6491fd8c0484ac36099c23161dd7a2625","tests/fixtures/invalid/encoding/bad-utf8-in-multiline.stderr":"2328a89cd9043de10ee656f4ea0dd5e6491fd8c0484ac36099c23161dd7a2625","tests/fixtures/invalid/encoding/bad-utf8-in-string-literal.stderr":"eefb00fee073933fbdb95d24a9e7050c281d4719d0cb970c2c06a71a86f108b3","tests/fixtures/invalid/encoding/bad-utf8-in-string.stderr":"eefb00fee073933fbdb95d24a9e7050c281d4719d0cb970c2c06a71a86f108b3","tests/fixtures/invalid/encoding/bom-not-at-start-1.stderr":"bd4e557b8b4586cdb39a8fde46f0bb214954f9f8ef37be46e2cc19823f6d6919","tests/fixtures/invalid/encoding/bom-not-at-start-2.stderr":"27003a498cb355011782dc21f01e15457490b78c472bb9ddb54147413c8f597e","tests/fixtures/invalid/encoding/utf16-bom.stderr":"a8800edcb8f6184b712da53e74bb787c39eb891073575acbae1ad575f15043cc","tests/fixtures/invalid/encoding/utf16-comment.stderr":"edb66c01034865f484ccf7921bfcec1efaa8599762cb9cd30c9c8103275bc4e6","tests/fixtures/invalid/encoding/utf16-key.stderr":"fb6a1d4cc571ff9d1c154f0a996d61d8f260d7e505318d02c0a03b38e2414e5e","tests/fixtures/invalid/float/double-point-1.stderr":"2917901dd186adc39cb5965faf388fa2babe577ef3bfcadd4919232868a727cf","tests/fixtures/invalid/float/double-point-2.stderr":"7eda489da0436d6f0f2268aa4005b422d215b4785af0c1696c8731908a563f17","tests/fixtures/invalid/float/exp-double-e-1.stderr":"e64082e328fcfbeff57e6801448c769b12bc8e879b77421b688b2e147e386713","tests/fixtures/invalid/float/exp-double-e-2.stderr":"5c45326ef7287ea16a9e08275222e281b5d61c9322f8764f6533707f9772e255","tests/fixtures/invalid/float/exp-double-us.stderr":"ebd30aa3f7cd3a0a5e79bbbde1beff209d24f4ab58eb5552c1baf0eb2194e97b","tests/fixtures/invalid/float/exp-leading-us.stderr":"19c8f676dd45a5db09bd5baba5c3e7b661e83099a340331ee6bb10defe679569","tests/fixtures/invalid/float/exp-point-1.stderr":"23e73e4e63db888546866967a1c0319a1db269f23ee9c277b298e9f2db88800e","tests/fixtures/invalid/float/exp-point-2.stderr":"633328e085fb04d6a79cdfb696f45a1836c3a8b6afafc4cd5e16d48465aa4613","tests/fixtures/invalid/float/exp-point-3.stderr":"b8b25bdb9b28cbe695134f00fdde85b5fa79b28408831356dc3a62650159a152","tests/fixtures/invalid/float/exp-trailing-us-1.stderr":"a73b4458debd1d0f750a6c5ade21b4046dfa2fa32b7573b0dec46f9400ed4f57","tests/fixtures/invalid/float/exp-trailing-us-2.stderr":"7a62522d55afc9bcc3ce5f11a9de8a62765803a99db4114ad6a2a5b777a302ab","tests/fixtures/invalid/float/exp-trailing-us.stderr":"aaae81eba820233944bb88920621dc9c1bbd0d1a1476c0376a38d0491a30c83e","tests/fixtures/invalid/float/float.stderr":"cc664d16849deec2ae7ebee6a3f46923bd5959075e282315c4f60461cdb13a0f","tests/fixtures/invalid/float/inf-capital.stderr":"1451a322f3be80529ebc091d231b682127e783a07cfffcce67f5b9bb4455c0c3","tests/fixtures/invalid/float/inf-incomplete-1.stderr":"38cd906dfee7f13b8cbdb27f3406ab0499fae3ae16f3c77bc7fc48d009595d93","tests/fixtures/invalid/float/inf-incomplete-2.stderr":"97a9ae1ff194a95b5be2abaf2cd8179ada832cdd9fad349efa9951e7ab92e435","tests/fixtures/invalid/float/inf-incomplete-3.stderr":"034bc609343ecf1e659d6250f719e5f93512e8140228e44e57b538765e58a1f7","tests/fixtures/invalid/float/inf_underscore.stderr":"621326dde26e5364c7af1b562fb651f4184d9b5fc9bc45edc12f52b588d506bc","tests/fixtures/invalid/float/leading-point-neg.stderr":"d19e28ba2f11069800df4dd1951025aa7f75425f7258e8caf4bbf6abe0e84bc9","tests/fixtures/invalid/float/leading-point-plus.stderr":"10750e9acccb17f0682db30fb175d083d06c822a4863d3d6b8ddb6c75b7b22ec","tests/fixtures/invalid/float/leading-point.stderr":"2545b7a615528595f5d53a7338403c83a8587e70600b1501225446e5f456c805","tests/fixtures/invalid/float/leading-us.stderr":"dc958138922097b2e1e3865c7818604b2249268af4acbe5cafe0ce8c68a90a86","tests/fixtures/invalid/float/leading-zero-neg.stderr":"d1fad35fa8d18f93ebfdf681d3476f02600e5c39cc942ca9bc36181476cbbe53","tests/fixtures/invalid/float/leading-zero-plus.stderr":"ad8ba7a7c12cb4b296cc0d43915106732e6a6a713aea67034587d1fc0c8093df","tests/fixtures/invalid/float/leading-zero.stderr":"cc664d16849deec2ae7ebee6a3f46923bd5959075e282315c4f60461cdb13a0f","tests/fixtures/invalid/float/nan-capital.stderr":"d0e9234b96d4e3591ca0190a785789f9bdcaaff01a111eb57db7bc458a2dd95d","tests/fixtures/invalid/float/nan-incomplete-1.stderr":"f4bee0b1c639bf800fc4dda38276142e715cd85ab6cc5e93ae2112ea63d7de89","tests/fixtures/invalid/float/nan-incomplete-2.stderr":"dc908ec577d29083bfd709fc4bdc2fa641d7fb2ba77a5d7441215680a8839d69","tests/fixtures/invalid/float/nan-incomplete-3.stderr":"abab5a41e0f2f1bad2d2050d0c913dfd8c15e50530d53ef8de327f106f564e02","tests/fixtures/invalid/float/nan_underscore.stderr":"25b67a7d6c743f673be7b409c9990de5de8b52a1d97c32e6f4e62f33147f1872","tests/fixtures/invalid/float/trailing-point-min.stderr":"69ad03ae81990d580a6d63bdd5ab594de00c0a16694c8671704c6243b4578b38","tests/fixtures/invalid/float/trailing-point-plus.stderr":"fba0bbad890020fe943e9f23644e81bf0bb7d114230fe16182e866fddcfc108b","tests/fixtures/invalid/float/trailing-point.stderr":"2f12b368fd94304ab0126ebb5888c519475f9ca28e9ca702c477cf0085ba9216","tests/fixtures/invalid/float/trailing-us-exp-1.stderr":"217939411cc9c99589d1ef0f3919c90ca2a562c0352063aae08ba2ae53c1208b","tests/fixtures/invalid/float/trailing-us-exp-2.stderr":"ecf0002a04040c8afcae7f4bb182c0322b4d00ab88bb53405e40c7938f2a9443","tests/fixtures/invalid/float/trailing-us.stderr":"506cb8051f1045ea1dc7f11865d58cbca0216502d273e1c10366c8be7cc9ab43","tests/fixtures/invalid/float/us-after-point.stderr":"fa9fb59f703b6770be3dc094c04eb2c4add8a7a7ab79d9fe508cfeee785404f1","tests/fixtures/invalid/float/us-before-point.stderr":"14e09a7a382e249e5143d1c81d6e4623408eb2d505e1e3f86c370a3a3bf6cd9e","tests/fixtures/invalid/inline-table/bad-key-syntax.stderr":"cc3565bdd7ce5752ed2e0aa6ca10e8a414d357a1f5630d7c759f8ffb709cc540","tests/fixtures/invalid/inline-table/double-comma.stderr":"2132a1c4d97fab140089818f990284333e22ef91d20a9f65e11d4dd15b1a701a","tests/fixtures/invalid/inline-table/duplicate-key-1.stderr":"72bea73b20005f15ced977aae70a1b0f3bbe3e35598231aca9a2303d770efdc3","tests/fixtures/invalid/inline-table/duplicate-key-2.stderr":"9b69c8521345fcc21886138d9dd0f20528c71712f3de3e565087adc916113a07","tests/fixtures/invalid/inline-table/duplicate-key-3.stderr":"675b84acec95eb5778a8d280881fd83cc3f741d0c0e2a21cd74fe1ab2b6bd710","tests/fixtures/invalid/inline-table/duplicate-key-4.stderr":"5ed3af9e2011f07a04572b2bcc1dd11dd57512dd35ff616e37051a34bd1f4a94","tests/fixtures/invalid/inline-table/empty-1.stderr":"604fef40337f04e5f37a52239d6509850aba95677a7a94ca8476a6c21b648a43","tests/fixtures/invalid/inline-table/empty-2.stderr":"870f189adbefaa6f05307d5b00748f1ae1c748d96105fabeb409c3c7d59126ca","tests/fixtures/invalid/inline-table/empty-3.stderr":"766b0f61a467c8db42514016a9a3812f678b054a124361bf7b7617cf2ae073db","tests/fixtures/invalid/inline-table/linebreak-1.stderr":"45b0611d37c1ece88bf6c88b3528adc3d73e0cd3e3b24dcf07ab151023a6f488","tests/fixtures/invalid/inline-table/linebreak-2.stderr":"f7672965326b44adaf0cb4796a087fbe779a8b17fbb458090a33375d0c54e5b4","tests/fixtures/invalid/inline-table/linebreak-3.stderr":"e8c70f0c91b15e701567e93d8df1cd3bec593696af05ec1d95e8f9e00ab20fa6","tests/fixtures/invalid/inline-table/linebreak-4.stderr":"3d31147f9e1ff5f94384e4df1675dfff2da6f076cb0a729771615f05b990be91","tests/fixtures/invalid/inline-table/no-close-1.stderr":"0bcaf312d65af22a37bdd8334821d95d4212dd43905fc945ec2c8ad3d465ae7a","tests/fixtures/invalid/inline-table/no-close-2.stderr":"4c6bfd3dd94611a3bc02fc609a738bc252dc38501e7ef6ff19543d757cc564e4","tests/fixtures/invalid/inline-table/no-comma-1.stderr":"9f1c85e0df72c7e7e011c26a0d5dd9dea8b7a5e18c3ba9a53ff4a20a9429dce9","tests/fixtures/invalid/inline-table/no-comma-2.stderr":"24a06e43a94ab041395eedb94c5bdb799ed7fbf6d930791756b0e3bd4a812943","tests/fixtures/invalid/inline-table/overwrite-01.stderr":"812d1bc74d07750048a521e513a565676e606d4fa1a32d2ebda7af8fa064d3ab","tests/fixtures/invalid/inline-table/overwrite-02.stderr":"bf95d34749254300f4179ed1314cc9cabd7c7b63fc2453fc7adbc7869b63be4a","tests/fixtures/invalid/inline-table/overwrite-03.stderr":"3e2c2ce66f1e4982aab428075105a39b2e9384f2dcf6da6d715017533416e149","tests/fixtures/invalid/inline-table/overwrite-04.stderr":"8aeb424d4ccee35ae17efba8acd65aba834192672cad73e8e1e6c3fe9f878826","tests/fixtures/invalid/inline-table/overwrite-05.stderr":"ff8facd04689f13ec53ee77bc9790d25e2d2eec50f4675788d70a2bf33a85e2e","tests/fixtures/invalid/inline-table/overwrite-06.stderr":"01be3b4e31d2c0aed381fc6599d5fd78d0757e9b76e66b087e4614e98f782db3","tests/fixtures/invalid/inline-table/overwrite-07.stderr":"52bac538099395ae15c5c8786f835dbab4748be0464951a25ae0f44aeea90125","tests/fixtures/invalid/inline-table/overwrite-08.stderr":"bbbc4b8959113590e9bc27526b56bae2e1223c88493836ea0a0df4209527a038","tests/fixtures/invalid/inline-table/overwrite-09.stderr":"1b084659999a2cf01ba8feab1d9d9232a307fae584e4277210cee69f53ef6cab","tests/fixtures/invalid/inline-table/overwrite-10.stderr":"229695997044ce82d3c3979904401a44838ae70103729b6ebba468cfb1beb154","tests/fixtures/invalid/inline-table/trailing-comma.stderr":"4791911dafd6602e2891d6ffc4d32ef8e9d0c1f8f6d37e84d440feb896d9cb88","tests/fixtures/invalid/integer/capital-bin.stderr":"fcfc8b0bddd36a641d3f5cc2ceee88554619fabf6874e11cdfdd147be8781881","tests/fixtures/invalid/integer/capital-hex.stderr":"c8e2d64f9659435a0387bb7e6447896eda253fef77e0214a4073fcffbac693a7","tests/fixtures/invalid/integer/capital-oct.stderr":"ec465fa25da212b0c9b6265ac8e9cd05c1fa07d614dafb3bc9b2ca74d6c2a7a7","tests/fixtures/invalid/integer/double-sign-nex.stderr":"8d57da526240c1cf73423b688442922ae291ff26e3c09f9c3b5b150e62e5cbaa","tests/fixtures/invalid/integer/double-sign-plus.stderr":"55896d9bd19637e124482966a12109a1a8351620ddc6f8d28553d70359f523f1","tests/fixtures/invalid/integer/double-us.stderr":"f14ed7bd3ad26b2203763fa953dd6e99212e50fb8e43a4eaeb115c1a7df4fc25","tests/fixtures/invalid/integer/incomplete-bin.stderr":"64168fc7ede87a10c12f82325fce644a7d9b9c3af55a313184175df7926845e3","tests/fixtures/invalid/integer/incomplete-hex.stderr":"ed2423540e288f4673bc68822a799bea04f571db5de56154e10360b03ab79553","tests/fixtures/invalid/integer/incomplete-oct.stderr":"9ed35e3078703a38996f20dc3e86477149564c8abd237c644bdf3a5ef26e3417","tests/fixtures/invalid/integer/integer.stderr":"ed5ef991b733b3d51700364da18bf58f1b7eb68053467afcbff22775b3b82788","tests/fixtures/invalid/integer/invalid-bin.stderr":"7248d47f2c7db309254a3a41af28bc1a6e96bfa95e0c8c94d607f65a1a30cee6","tests/fixtures/invalid/integer/invalid-hex-1.stderr":"ca2af571a835ca976727c823939f7cbd6d36f7d048464ba1f8f0bc6b6558cb57","tests/fixtures/invalid/integer/invalid-hex-2.stderr":"e2970d46eadb852d34a8407929972acf7ef131c3c44978af0e6dfe205a6e993a","tests/fixtures/invalid/integer/invalid-hex.stderr":"3976255c6fe35a1e29f0fed7324eee8420ababd0f6f1f7702908c3df47c88846","tests/fixtures/invalid/integer/invalid-oct.stderr":"9f6776e33887cb446a5590d8fe4e51c36747c634cd5e4efaa84f807d3ce244e0","tests/fixtures/invalid/integer/leading-us-bin.stderr":"0cb1db77dee877423738395a720e6ebbd5a545a3b22ce710ab669b5b1f7903f5","tests/fixtures/invalid/integer/leading-us-hex.stderr":"fec78f4fe4ad481fe9ea93465c8ef5bca8b98d0bba31b48b2990870b7aa5f44b","tests/fixtures/invalid/integer/leading-us-oct.stderr":"aad69bdd80f94e907bda03558a1302e54d58d8911fe2b564e93cb0ec48403b09","tests/fixtures/invalid/integer/leading-us.stderr":"3a265cc11f1b0d43d4b532a47776486ec7c7ea7afe70813ab00c5a37cf87a9df","tests/fixtures/invalid/integer/leading-zero-1.stderr":"ed5ef991b733b3d51700364da18bf58f1b7eb68053467afcbff22775b3b82788","tests/fixtures/invalid/integer/leading-zero-2.stderr":"5c70e7874256512c0ef6bb364497d4e10154e994056f2feb7c5c729016522091","tests/fixtures/invalid/integer/leading-zero-3.stderr":"fb2730feda6f669a3b8c4332f01369e52ce1b942807f1bf3d9762b1fea04aeac","tests/fixtures/invalid/integer/leading-zero-sign-1.stderr":"c9d2d992eea36c4fe228eb74741bd8d0ede1e354cad132b79462e7b502b37f95","tests/fixtures/invalid/integer/leading-zero-sign-2.stderr":"4248329b339020cc2ea586f2775a0b4f4cbe2ae3f0f75b935263363b8be5eaf5","tests/fixtures/invalid/integer/leading-zero-sign-3.stderr":"3b414808727d3a446efdfca0033525e17536f9b54104d8a9cb9278b054d213df","tests/fixtures/invalid/integer/negative-bin.stderr":"74aae673b861bd46544e4835fe7075e20158dd69e27f75c790d48a6006476c73","tests/fixtures/invalid/integer/negative-hex.stderr":"799bd8120f4cf2c36e7f65a5f9aa43a3ec87dd95dd3bf68501059da9f21f8c9e","tests/fixtures/invalid/integer/negative-oct.stderr":"017a6a24faf9dc1cde89f62b46435f8fca493e7b61f6fbd2b6d57f0f9e80da65","tests/fixtures/invalid/integer/positive-bin.stderr":"54d8a33743737f374480cd1235bf3f7e0847d252ef7e2bb1d447529cbc0f6692","tests/fixtures/invalid/integer/positive-hex.stderr":"3b21b23cc3dd6b213a19256f4ffb4bb36172de2f739f90bbea78636f7a50524b","tests/fixtures/invalid/integer/positive-oct.stderr":"f19faef5bbb7ed8351777bdededb1c523337f2aeeec82d967c19c36069790e11","tests/fixtures/invalid/integer/text-after-integer.stderr":"07a13ad4841a452eff00947234a4ebac4d209ea0294162888db35668648bb55d","tests/fixtures/invalid/integer/trailing-us-bin.stderr":"62da06cf06527b9e9cbeba6c5299ce6001d40592e9d007c8350090977f4d1b58","tests/fixtures/invalid/integer/trailing-us-hex.stderr":"1b290eada58a7202b1a9251afd8e0e72a4caa8ad5c85036d1050e7de8141e94d","tests/fixtures/invalid/integer/trailing-us-oct.stderr":"34e6f86ffb0099e6e1ba67deb51e36af62dfce4e7299b94503a219339bf16447","tests/fixtures/invalid/integer/trailing-us.stderr":"3ab49ee921eb772f5aa4eaf0fb3619b1dcd9a9db3f4ebbd9bc505581a985e753","tests/fixtures/invalid/integer/us-after-bin.stderr":"a94a87ebab3536899ce7c0c785f020b3a236c60d24c0bd7494628ca310c40768","tests/fixtures/invalid/integer/us-after-hex.stderr":"9009b187f615f06e3392eabd8ffa58311ed1c2b1cd76f8c5bd99671242f2e026","tests/fixtures/invalid/integer/us-after-oct.stderr":"05af70a21980416fbd602337f9af22a1c600a294635d10ef1ca1b2138338e712","tests/fixtures/invalid/key/after-array.stderr":"487d957b20226ac36e27d6efb1e3d24147284c9a5e10a0188427a1c940d31ef0","tests/fixtures/invalid/key/after-table.stderr":"f70e84770817f096fcc1b6195c6b0a79d25210c6930ce412a89646040ee3d713","tests/fixtures/invalid/key/after-value.stderr":"00d4d2d3ccd61f64a92df0ca575aeafcd96e91d053d835ca855973339ba458cf","tests/fixtures/invalid/key/bare-invalid-character.stderr":"b1f64d54a43017e6cc09755fa7ba477901721d23f9271ec658fc9362f46631b3","tests/fixtures/invalid/key/dotted-redefine-table-1.stderr":"59771f7163f28a3c81209f058b7b01d616fe5022e8ee7ffb395feb44129cafea","tests/fixtures/invalid/key/dotted-redefine-table-2.stderr":"564febb355d1556df42f428a046ac6fdc5dad49b2b736be5824b0c13fcd1fae9","tests/fixtures/invalid/key/duplicate-keys-1.stderr":"73407dfd58ba687026376cc491a42bdca3b6c94a1a85ed2a6884a7fd116acee1","tests/fixtures/invalid/key/duplicate-keys-2.stderr":"7c9dfef2ef19b1487b7592a267ab5ba21c8b833dfa9ec1c3151e369c2fdba26e","tests/fixtures/invalid/key/duplicate-keys-3.stderr":"5d84bb05a826ef44134351dbace41f3d771a2d7ff3777dbff4dda8c1fe18ad62","tests/fixtures/invalid/key/duplicate-keys-4.stderr":"522ae99c93599d13c0f1acc1fdb4f3d1787e83dd379f34f07ed5cf1f8e92cbf0","tests/fixtures/invalid/key/empty.stderr":"af6d3636ca73e5496c40d9c918c59b61fd86812db262649e5268094193873130","tests/fixtures/invalid/key/end-in-escape.stderr":"86b9c28ffc74797d35a89fee58853fa85bab9638b919138ccd5d8dd524dd204c","tests/fixtures/invalid/key/escape.stderr":"155aa9389f0eb28cac3b42974af7ea9e2eef8d96f084f08f9f75e960fc8ce8c7","tests/fixtures/invalid/key/hash.stderr":"85dd91b96aa4f81cc7922b02b411f25d9053bddd1e5b893c2a2ee9d0115a7cac","tests/fixtures/invalid/key/newline-1.stderr":"714aed0a140062f977ec85b9afa50f68448c67e806168e60b4f4554ab270b2b9","tests/fixtures/invalid/key/newline-2.stderr":"8bdeb90922617a8e943e0196b929e62a7e30baebabd49870f5c31adb77ff93a6","tests/fixtures/invalid/key/newline-3.stderr":"3862b1f006b761fcc0c97166b91a20d57ba74d4da39f2590b7b5bb2493a8090b","tests/fixtures/invalid/key/newline-4.stderr":"d625f2caaf01d53d72d6f1c3df0952fe3ca8c5f3b081503cb02b9994c088b733","tests/fixtures/invalid/key/newline-5.stderr":"6d811add45be91fa4debb461126613db9c605bf758a21490db0024ed3887ea4e","tests/fixtures/invalid/key/no-eol.stderr":"440ec927e94f0e520a0f256c865041f0478e1c82f3bb79323b7ddc36fc942edf","tests/fixtures/invalid/key/open-bracket.stderr":"3b36814373f51a8ea00a448d65bc514e8d99f5163b7dd8101df62bcd0a06e801","tests/fixtures/invalid/key/partial-quoted.stderr":"dc9059a014ed53071ed170b1e280923556dc09e0be2ae96cc8474e9da59fa378","tests/fixtures/invalid/key/quoted-unclosed-1.stderr":"6cdec8a7c5352a2f246273afaa923dfa81d4d2e68cca5b4f9a19193559b164c2","tests/fixtures/invalid/key/quoted-unclosed-2.stderr":"b4817e6f85a90fbb6adf049ba57c268f9888f1b42b3d62200c359606176170b1","tests/fixtures/invalid/key/single-open-bracket.stderr":"917c0203d1e45309fcff82ce33fdd2d989f630fb99290a40cb9e08a6f7ca0ef8","tests/fixtures/invalid/key/space.stderr":"3a5fa712d667890678873e3d4e4cabb084c67091c5ec6155355d5bd4229585dc","tests/fixtures/invalid/key/special-character.stderr":"a84c2f293c1e421a1c87050cb0211de80dbfe7a79939db0338fa35bf0c181ef2","tests/fixtures/invalid/key/start-bracket.stderr":"223d8a22bf34459cd9bcb993ae2a51ab3cc436674e3367e92f7d74e9f8710a45","tests/fixtures/invalid/key/start-dot.stderr":"f9366a1492ae24fd0721724b4039d2675e91219de564aff2826adefd83fac571","tests/fixtures/invalid/key/two-equals-1.stderr":"a0aae899cfa75df41104a4d3090a309fc7ebcd95bb5a944cf742f3d3fc9d4782","tests/fixtures/invalid/key/two-equals-2.stderr":"861826b9456ab3a74f63f5c555e13d959a3991dfa6ce126ae5ed14d43f7dcee1","tests/fixtures/invalid/key/two-equals-3.stderr":"71614864344e321ac5de238b7ef9d097c6d7f3ac3eee4118d96827b4b8bd6658","tests/fixtures/invalid/key/without-value-1.stderr":"16c2823a39a82c3c27e0959a691b7a95e3392d62195884697893d373b967b9c0","tests/fixtures/invalid/key/without-value-2.stderr":"d340f94f5d96f5730ab269db7ef27aca171d64e35af1181c474d75a7d11d6590","tests/fixtures/invalid/key/without-value-3.stderr":"3cf3072fe9206bfe6c682103d0414627a5a63db4c4a319cf37efeb5fe6b92007","tests/fixtures/invalid/key/without-value-4.stderr":"07132bec96e9a9a672bafdc3c448b7c596257245f8c3e2cae04641f9798644ec","tests/fixtures/invalid/key/without-value-5.stderr":"37dc02af5ab8a30161223d44ed05c99ba742a658598a1b94ff78ed09afd9b11b","tests/fixtures/invalid/key/without-value-6.stderr":"00b623259f9c58fdfbe6753978fe2a71653bed0dda5c4ce54cb2151e8f7a6a29","tests/fixtures/invalid/key/without-value-7.stderr":"5013036b7f53013f887f670c3a3ceca6358d89e6b83b27bea9aa4447fba083a4","tests/fixtures/invalid/local-date/feb-29.stderr":"49fc14bfe63430553b173e82b84fb964a8bd93eeaf8abb28ed94f92a061e0026","tests/fixtures/invalid/local-date/feb-30.stderr":"1ae91b3300919e07b64c5c5b6572be05dccba63854df52ed71a900d190a90900","tests/fixtures/invalid/local-date/mday-over.stderr":"851f565b0537d5f2d88829c62d632d7dc5841d9843a1244568ea7382d5b05857","tests/fixtures/invalid/local-date/mday-under.stderr":"34c1e1d9e5839a2a8ccfaecbf52b3f49e1a776cb17de9187c3e79cb618ac684f","tests/fixtures/invalid/local-date/month-over.stderr":"d6ec78690f874b4c99cde7e609775abdf4f00ba758216afee355f6baa2c7c010","tests/fixtures/invalid/local-date/month-under.stderr":"c6922666b726822f6ffeca857041eec16cf387f54d3b6d9c325935c1c116aa5c","tests/fixtures/invalid/local-date/no-leads-with-milli.stderr":"03ff73112eae42e69f54f80445582775309400ce7f179bd9d28043299d5da826","tests/fixtures/invalid/local-date/no-leads.stderr":"12d98e610ca2c2e04fff563f9ba2b12f5e53937df1ced544c8082fa4f64522e0","tests/fixtures/invalid/local-date/trailing-t.stderr":"87a15cd62bbe7cba2b942fe424c045ce30a12fe439a1b49587a5cc037ffa6b09","tests/fixtures/invalid/local-date/y10k.stderr":"7c5cef8b079e2e6adbb7c723a1e75f04ac1185975113e79ff6a6352e07908867","tests/fixtures/invalid/local-datetime/feb-29.stderr":"692c0e78fd9ee1e979dc32bed3ed9d3d10a4608090b4d242e379d40a9732c2ab","tests/fixtures/invalid/local-datetime/feb-30.stderr":"330e20a8e3d95195227c3e66861f013b70fc02010107c9d0a11085bd533b49cc","tests/fixtures/invalid/local-datetime/hour-over.stderr":"a095bf7be4a76d0c9362d5e4398d5783aa1545ec03f910a2c6a1709a3f60225c","tests/fixtures/invalid/local-datetime/mday-over.stderr":"cd707915cf15690dcc65868408f115bc90da19beae1fbd0a7fce8d02ce76300d","tests/fixtures/invalid/local-datetime/mday-under.stderr":"fd8ac4e2804d96d6b7f63eb1616096bdcc3ecc2b74d75c3fc4bd98f2f3af205b","tests/fixtures/invalid/local-datetime/minute-over.stderr":"95fef6b0821ffe465ea5ce27a4f1521a4309cc64ee03e2781e63ead3003cfc62","tests/fixtures/invalid/local-datetime/month-over.stderr":"f739e1cb0538ab5e3cd0923165d54bebe10a6cee1cd773129230d508af946901","tests/fixtures/invalid/local-datetime/month-under.stderr":"cc1749c4aaa0cec5e88a2cae52bf84da2e862b6d2dae2c46615eafa0b7f370bc","tests/fixtures/invalid/local-datetime/no-leads-with-milli.stderr":"d1d95c88dcbde166b3a89c562e87bb9dc7c7d4efb5aaf999f0c253b1109ffd2a","tests/fixtures/invalid/local-datetime/no-leads.stderr":"8be255d1f994769e4cb9fdc6b4b6d65ad2664348026e409026421be423150076","tests/fixtures/invalid/local-datetime/no-secs.stderr":"019be93baccb4283a186cfb846b8cd4c848a373319daf4955ab3bec451a7b730","tests/fixtures/invalid/local-datetime/no-t.stderr":"eca57151e4e310f22620f243582486fb3e5ade13e4d4a5fb4582252fd915ca04","tests/fixtures/invalid/local-datetime/second-over.stderr":"dad38092a29679601af7ae3b05960068fa0eec5a649858ab88aedaada6ffff00","tests/fixtures/invalid/local-datetime/time-no-leads.stderr":"b3282cb32386dd84a35468f488be5a92dd3488e951f9dd2ea39057046386b73e","tests/fixtures/invalid/local-datetime/y10k.stderr":"ebb06f456d5e909af5f41fffa29c3f42eea70d91a97769716da5c75de9f75ac0","tests/fixtures/invalid/local-time/hour-over.stderr":"e5aefc4baa094437e16b5a78c89df8469ac59d899433e1c0fcbf433344b3a4f9","tests/fixtures/invalid/local-time/minute-over.stderr":"b66ec93e63f27d0a4e63c0eb1fc2d3f8dbf2fc916e213192852b1b7ac0e21aa7","tests/fixtures/invalid/local-time/no-secs.stderr":"d9939a2e05d15375d4936c6ad7c4dbd13085e638573094f3a0befd9027c0a3ab","tests/fixtures/invalid/local-time/second-over.stderr":"f14b188fb85d8818ab953f2fa0d0b8de906dddc2f0e0ccf5ef68b3e81d619f20","tests/fixtures/invalid/local-time/time-no-leads-2.stderr":"f725d49ddb5af69b7285f071d68e3d8441d5e331adcfd8c025c1f3cbb68028a5","tests/fixtures/invalid/local-time/time-no-leads.stderr":"cf06f3847f3d14655a94d8cfd5a6984dc74115b1d3cdbee0662ef215738bbf65","tests/fixtures/invalid/spec/inline-table-2-0.stderr":"5ad1a938b1d1f0f3fdbd1871efdebfd30e136407ecdd9e2eff22150d00624b3f","tests/fixtures/invalid/spec/inline-table-3-0.stderr":"fcbc05e911b7db81bd918768fe98a51a7026fd476d616718cc417d2f08bcc1a1","tests/fixtures/invalid/spec/key-value-pair-1.stderr":"d5391142dfd56040840cf91b1e28e3c048229e3d9998534d41001cd6657f9bd6","tests/fixtures/invalid/spec/keys-2.stderr":"3c4ee6066fc75d2c1f1b325f618a01113694c318e330ff4f237e89127f332c87","tests/fixtures/invalid/spec/string-4-0.stderr":"910ee4b240159b828a7509c8dfb46507071a8d8636f3935a3914d6d91f315295","tests/fixtures/invalid/spec/string-7-0.stderr":"5128f0a930b3034e494a6bee4f384a587e9fd858b25f8cc529a488c94ee9670d","tests/fixtures/invalid/spec/table-9-0.stderr":"49dac70d337266f5c6b333fee468f279fed1bff62bfb4ec7436c8b6683ce0dd2","tests/fixtures/invalid/spec/table-9-1.stderr":"d9e071c70356c01b6537f876989ad2067e7773dd5eb24a298439d192dbad12d0","tests/fixtures/invalid/string/bad-byte-escape.stderr":"14f6ae446b3b8cb434267eba11c6ec5a1badef4f867169b173698cf9f1a29d95","tests/fixtures/invalid/string/bad-concat.stderr":"499219633467b9174471db40543ca188e2b906c470e511d2f701f5f5475d96be","tests/fixtures/invalid/string/bad-escape-1.stderr":"34a15ce7012217c62d31d5392038517c216f0cbfd5d75fb5f3c2bb07afd3f25c","tests/fixtures/invalid/string/bad-escape-2.stderr":"955aab40b16043c847d85d04e6adcd093c930dd8416d29c2ab5953c077eac6f4","tests/fixtures/invalid/string/bad-escape-3.stderr":"ef8302d7a6f9b8beb54478756a6069dfafc203f640a4afa2a58fbf13fdb35b8b","tests/fixtures/invalid/string/bad-hex-esc-1.stderr":"aea935cf1e17743356e6fb1059afed2d0ee5262906594782e5537a025398038e","tests/fixtures/invalid/string/bad-hex-esc-2.stderr":"deac5217cf80acc759e1b40c43f5f56431b276dc2c896aae5490d57583105e06","tests/fixtures/invalid/string/bad-hex-esc-3.stderr":"94ecf886427e8fe5daf1d8f932bf1887f2533b10bc1f57cb6de03ea28fef466f","tests/fixtures/invalid/string/bad-hex-esc-4.stderr":"382b011dd4070554ee875fde06703d8332ef6ad36f3619f3536b0a4997ee2745","tests/fixtures/invalid/string/bad-hex-esc-5.stderr":"a8a039fae822eda68591da28ff2a117b5d85e99d066e9126ebbb6426a1cad52d","tests/fixtures/invalid/string/bad-multiline.stderr":"141e5770190dd184bb1f64f6bb14fc017210bbd918ab5c8b7a3d80b86b21772b","tests/fixtures/invalid/string/bad-slash-escape.stderr":"d62f894ee166bddf84432507fb4ba56473c0a230fd88a3ccc2b199a72a34e613","tests/fixtures/invalid/string/bad-uni-esc-1.stderr":"b7d8a7f41600a6fc5cef5fd938fab31e1a516b5075bb5f6b22ee77e49bbeb195","tests/fixtures/invalid/string/bad-uni-esc-2.stderr":"5c23cec7a912ccba77180889e44dd84287fbbfdb170367146e9d633637124052","tests/fixtures/invalid/string/bad-uni-esc-3.stderr":"744574793d570b012ee2aa537405af14612183b769e425a04bd0c0ec6e14da7c","tests/fixtures/invalid/string/bad-uni-esc-4.stderr":"16543872b51db7ff6a87cdf1ae71917857e5118b90ceb3e0835525c9bd67d02d","tests/fixtures/invalid/string/bad-uni-esc-5.stderr":"f0ef02d2988680da67942d8599e7753f2e6c89a984643000a67ebf4c34722374","tests/fixtures/invalid/string/bad-uni-esc-6.stderr":"eff7b6dd907132aa9598dca52bf12d48be066a6a8d426ce95d5f4b344bfe8d98","tests/fixtures/invalid/string/bad-uni-esc-7.stderr":"24cd2f58919584c4dd12f0262933c8c0c6142a2b62c747d465ca1b9b4986093c","tests/fixtures/invalid/string/basic-byte-escapes.stderr":"b42fd0273c7438bf13ddea9552204bb9209cdcc8e4151311d2446185d2cd546a","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-1.stderr":"725cd4955987c3d6e736832281316d6c1a2446303e9a1dc78900cef4bb84ee64","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-2.stderr":"c6698fbdb95188d53bfdaa4a4f590d86a73aafcc321a5d9511ab43ce51be1c78","tests/fixtures/invalid/string/basic-multiline-quotes.stderr":"28177a49532f22aaffc9dc204592a2c5eca2fc20f8e208b7c7f589201e8b7de5","tests/fixtures/invalid/string/basic-multiline-unknown-escape.stderr":"a83406b30eb3ab2cebb0230d8d65d0b7583885138f2c070976ae61de2c8b17f3","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-1.stderr":"19af67599c6c2eef340c9fdb0ab2cc788928def50280af939247a1274447781f","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-2.stderr":"0e2e1a69358502ec17a07e4fc151b70e8a3b5123798cb38f98fe2d146515a84e","tests/fixtures/invalid/string/basic-unknown-escape.stderr":"1de467948fb18f61336350063701d9c5a6615054fe740a9be650f71f5ca4236b","tests/fixtures/invalid/string/literal-multiline-quotes-1.stderr":"249123229606aa8eedff1b5bdead5022daf470e47dbca639e32019d1d61dbcf9","tests/fixtures/invalid/string/literal-multiline-quotes-2.stderr":"d9784af1ff056a90bf531307749d53a5d24ffffbc0f4aada7fcee417a50d1615","tests/fixtures/invalid/string/missing-quotes.stderr":"462f24701d2c51d36b18d06b69be2f6eb36449b5f3ffbaa737fcbd2b2151ae4a","tests/fixtures/invalid/string/multiline-bad-escape-1.stderr":"18469c4d37d011b3f30ae17e3111b5e8a9526d593475e5d8d7a9b19461a40e8d","tests/fixtures/invalid/string/multiline-bad-escape-2.stderr":"d43896d3005c8470dc8149e2b74eb8825c6d9fedfe9f48125ad88a95c1dc3035","tests/fixtures/invalid/string/multiline-bad-escape-3.stderr":"92f732c6bcb922e25d2a001a389f93b596dd0e91109cbdcb651efa146309dc2a","tests/fixtures/invalid/string/multiline-bad-escape-4.stderr":"5e549045d08a76406f29c10433766f1c32aa69a169f3dc138103b4615c9979b1","tests/fixtures/invalid/string/multiline-escape-space-1.stderr":"94b451b6c03055186777a248cb216f95a1b2e29df25549f345d96bd0a4e63f1e","tests/fixtures/invalid/string/multiline-escape-space-2.stderr":"850d75a72e44e1ceeced7d65b810afa5b2935f3ab3efea0f3e147a06d82a6637","tests/fixtures/invalid/string/multiline-lit-no-close-1.stderr":"8e81ec582e75388024196cb5a64aaccc128c874be341fba01973c722e64a0d1b","tests/fixtures/invalid/string/multiline-lit-no-close-2.stderr":"9572aa98be504bab0172f612c40010d005577a6cd30f7fb3d25ea29c73e0489a","tests/fixtures/invalid/string/multiline-lit-no-close-3.stderr":"cc89579aa2201fc3bbe98693f822516557d950a9844e68ace4355991dd02478b","tests/fixtures/invalid/string/multiline-lit-no-close-4.stderr":"2310b339cc17b8aefce9ef0e2805c77d5cce3d70abe93373ebb8f986169afe00","tests/fixtures/invalid/string/multiline-no-close-1.stderr":"d5b9602d23b0cb023fbe3ae80d862fd60332475ba8863a1e977f17cb326a4548","tests/fixtures/invalid/string/multiline-no-close-2.stderr":"e500e99a44305b1e148b211e963478cf1554f8c9536d3108390cf41d5b2ce069","tests/fixtures/invalid/string/multiline-no-close-3.stderr":"efea30ada8d63f3071be499d63a82729b8f2c3d5f6155a1dca456e7d790f84be","tests/fixtures/invalid/string/multiline-no-close-4.stderr":"30ce703d6a38d97861b37d8539648734145e6ce45c39b2dc8b970b7b34557031","tests/fixtures/invalid/string/multiline-no-close-5.stderr":"a8b6610cb194104520cdc9fdd140192b6b1c738a3fe922a9f40c21b91f82487e","tests/fixtures/invalid/string/multiline-quotes-1.stderr":"046956658c0a73e665e7a6a2044ff83c8efb8cdd8c2ab153c163eb1e61068c56","tests/fixtures/invalid/string/no-close-1.stderr":"3ad8aff0932d98592b808fc6f44fa68a854097f8025e92c11af1acb6de3d3cc7","tests/fixtures/invalid/string/no-close-2.stderr":"3438330fa63c592f316342c9786696a2426df2838d60ee52889a9dc2527ce77c","tests/fixtures/invalid/string/no-close-3.stderr":"06e62924f3e7a0d290ab2a377e6a6b96c69bafee7170feb37331d6220e956e38","tests/fixtures/invalid/string/no-close-4.stderr":"e5cacd943fec6e10430242c95b42cdd55068cc053f5b40eff38e077a70d0109a","tests/fixtures/invalid/string/string.stderr":"aea935cf1e17743356e6fb1059afed2d0ee5262906594782e5537a025398038e","tests/fixtures/invalid/string/text-after-string.stderr":"1c1e4677be8d3dba0e7933b3ed1cbb6e0bcf6f600cf9a989a7b09c9424a4d0a7","tests/fixtures/invalid/string/wrong-close.stderr":"441f4f1b73c11c8dbf2f73cf9a7766f17a9517b3b9142e86736ed43eaec07f18","tests/fixtures/invalid/table/append-to-array-with-dotted-keys.stderr":"e16e10a17e38898bfff5b16887d9143fa5036669e83b75e84aa4ba4078b1a9a8","tests/fixtures/invalid/table/append-with-dotted-keys-1.stderr":"a67f1f152005295e0a1bb3dcaaa755edd05f19ac5316b8ad2eb4d45797e0f770","tests/fixtures/invalid/table/append-with-dotted-keys-2.stderr":"72d9ea8a90b4d9e5319c2bf951bdde6a87a205612e82ed5a09cea2b706bfde7f","tests/fixtures/invalid/table/array-empty.stderr":"e8a41c60adf7756361920816b6c4f44125a813c869b71fae2c98473e4da1b231","tests/fixtures/invalid/table/array-implicit.stderr":"7797ce41aab0567fc9d40e277cc32c12e1f16ffc0e73857fdb3bbf754246305f","tests/fixtures/invalid/table/array-no-close-1.stderr":"5f1e8703d59398f6595d21ed0abcc7dc3ce77943ad0f71eede9ad63ea2bcc7c1","tests/fixtures/invalid/table/array-no-close-2.stderr":"5adeffef5a1e1d63b6461f2a734a5b557bd3709e4fde903262be0452890623a6","tests/fixtures/invalid/table/duplicate-key-dotted-array.stderr":"9158eaf24fd4237fb87a6fb9be00e18ea935cb509a657bfe370cfa769e97cef6","tests/fixtures/invalid/table/duplicate-key-dotted-table.stderr":"ca58908463cbe2ec6b3de314237c178fee64245cc738c72a7b9e08bb3d02b2b0","tests/fixtures/invalid/table/duplicate-key-dotted-table2.stderr":"cb59f2ed324642de947f3cd9373ca111ec35104a5f33578f64c48084ce1a84f5","tests/fixtures/invalid/table/duplicate-key-table.stderr":"f4816522738b3e2ace87d1100a3d73e6a122d8dc67d05e0b35a1438e16a8952c","tests/fixtures/invalid/table/duplicate-table-array.stderr":"11d293e4b4f205fc98cd892f25a25f533cb922c963ecf095a932d2e9d550be4f","tests/fixtures/invalid/table/duplicate-table-array2.stderr":"fa9cd3b1212eed14ec56b66a16471ac2f7c0398d743982abb7c5cb4b5c7a5fe4","tests/fixtures/invalid/table/duplicate.stderr":"3e6d1b1a2f44d449e8cb0098e7c40ad1e755363b446f3821c399abfb26eb9939","tests/fixtures/invalid/table/empty-implicit-table.stderr":"cd3606ce97c5537d18146cd978403636a65fa703c83616da75b8cafa86e8fa24","tests/fixtures/invalid/table/empty.stderr":"4399e419abbcfbec93f5915e7fbdd11b6e462a4c066a29eacda159abfc588734","tests/fixtures/invalid/table/equals-sign.stderr":"472de6b908a03c99637b635a3a898ed956684ae422e1b4b135ec94986ea45f2d","tests/fixtures/invalid/table/llbrace.stderr":"db6bbee7ed15994398901c46ed4b40904897e71f5d972deb7904ccac49cd834e","tests/fixtures/invalid/table/nested-brackets-close.stderr":"e1dff60ea8f77dd1b8fae7d1d63c788c838c80560172d92377cc168f5cb5923a","tests/fixtures/invalid/table/nested-brackets-open.stderr":"bd58eb0630dc0c51ebc288258d360d707c8f43a5877ddc21e9420f8eb76a2f4c","tests/fixtures/invalid/table/no-close-1.stderr":"ae6326db737d2e259c051fbe3f5aa6ef7d7ec1bd47930ea78e09667a20100a72","tests/fixtures/invalid/table/no-close-2.stderr":"3b599c6d07071a40a0c3acf163f0b04655bb8953fe32bdcab5e68a527c3ab22e","tests/fixtures/invalid/table/no-close-3.stderr":"6bf7e2d30c735a55f595140af7c7f6be89b6faf868f4473ea39570fdb87d5823","tests/fixtures/invalid/table/no-close-4.stderr":"917c0203d1e45309fcff82ce33fdd2d989f630fb99290a40cb9e08a6f7ca0ef8","tests/fixtures/invalid/table/no-close-5.stderr":"c1a691a6fa9638b75010f37166c29a6e5a2da2e35bd9a321118d7ea384af2d77","tests/fixtures/invalid/table/overwrite-array-in-parent.stderr":"300782a740fff829dfe485a4a43426a53f82bb6afca63ef82fc07081c43d8707","tests/fixtures/invalid/table/overwrite-bool-with-array.stderr":"dcd33263a49a91ed583c3f53c6e86d6c5b8d493d841aea074a5a81f57cb5c152","tests/fixtures/invalid/table/overwrite-with-deep-table.stderr":"b128988d3a37f5857c41751847ed0d9590e4cbda66a55238f73c60d992749e41","tests/fixtures/invalid/table/redefine-1.stderr":"3e794bce5bb6ae9f603f50e3dc62d136701ec478078e8a8e99c94229778e24ca","tests/fixtures/invalid/table/redefine-2.stderr":"76a6fa1ea8d5da8a78aecb88c506dcf4e07906984baa8c9d1a363b2bebfa4281","tests/fixtures/invalid/table/redefine-3.stderr":"6ebf320d6d2117c189dd8d425303a66739a4813e4abef2d3184dc9ef5915d959","tests/fixtures/invalid/table/rrbrace.stderr":"342a5ff362c8b4c1e85a6442029291bd33165a3b36552794fcd5269249bf36a1","tests/fixtures/invalid/table/super-twice.stderr":"78b95be29fe54e92ebc82d8b35e0d59f5b9796f323b7434a49daf72d0f0be02e","tests/fixtures/invalid/table/text-after-table.stderr":"6dfaf1fc3199f0602fea52f7b1c65869eb2f8643b9e90dc1e718a183fb972485","tests/fixtures/invalid/table/whitespace.stderr":"fa48d4dc83f92e729dc25c6fc6a0c336014391b4bdb3392998f18141d2deb350","tests/fixtures/invalid/table/with-pound.stderr":"97dbd1ceb7f357bd98cc1caa9a602c638aaa5831237b7d63b18153acc64d3af4","tests/invalid.rs":"daa9034453fb7f10718020e36a07a19664eb852071995f17480c595cb44e2cdf","tests/testsuite/convert.rs":"9140d681dbb9370b975d5bc1cd4e9e640ac4023c6789edcae544e66657ad5fe9","tests/testsuite/datetime.rs":"105d95570d05e9ecbc30bfe7d081f9d63e2f36634e9124da012f467c6134549e","tests/testsuite/edit.rs":"752f5310c253b670645335a316b789a71368ab3aebdf77ca2bfb6bcccaedc355","tests/testsuite/float.rs":"3db8b466505990ff3de38666c810b15c036632322b18f8e9c7893477dff4d641","tests/testsuite/invalid.rs":"31789643e3419ab922f8258e5a0421e1648b64aa5b96d3e1fb79bae36abf286e","tests/testsuite/main.rs":"b78ad4077facdf0e31ef77355fb3deb70d8339befbdb9ef16abca3b05231556e","tests/testsuite/parse.rs":"5697ddf398f2bea3396fcd24d7a21d32819849319f9db8e6cae4e7bf81cdf4b9","tests/testsuite/stackoverflow.rs":"426d4e621bbafe62f8aba2e8c62e715929185d5eca4c5083b6427b601abc667a"},"package":"0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"}
\ No newline at end of file
+{"files":{"Cargo.lock":"be642c4bf3ae8e4abd5feae72679e62da62b717b01edd404275ba8c1d1055609","Cargo.toml":"458a5d1b70969ad5895ea9d012a0d46342bf2960a9f82ad5a4f94c2d1963f55b","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"39f3575e0849928f6f8fa4cfff6f44c96fff96dc1146d761830a87a6ad33cdbc","examples/visit.rs":"e4bc255d89b1c01484cc50d116c559b4a89702f6e1e3d68459486059100f2144","src/array.rs":"5bbd00b103fb5c86f183453713137bb47cfee6d03514a381b4d1b7cfcad98968","src/array_of_tables.rs":"6f2a385fcc2259589f0b0b906aba66204900f02b052bcf3cddc45c507df39930","src/de/array.rs":"6d7d3099253e1519428f3e5af3d11ad625098552b199089a52b49ab940f55012","src/de/datetime.rs":"a29e76372fc382686c31ee93fd54c749ea3d775c8d4fcc2b2643f9464ea23730","src/de/key.rs":"26f5622ca99dedd8ea7fbee87289867e2e6fcf7cc2a6d565c61360090dcf5781","src/de/mod.rs":"166ab1170d838682058d1f6df881e567f6ad377dc0930be5dc4ea0812661dcb5","src/de/spanned.rs":"5c959639bf4b3d19c6b18e625a57b28b311164bf7b191bbc4d30aa97b7239679","src/de/table.rs":"007724aee900fa5e8800b7c69bb8fc0b8340e500297683d1b58e3a9cf6bfeafb","src/de/table_enum.rs":"ad3aab5169d6ddb2e7cd50a4dced4ee2485b11388876474969137b12fe6ee1e0","src/de/value.rs":"f2fb48b171d992bb189e2cfdd24c94a06b10498b269b7cc5a1be236aa6d34b65","src/document.rs":"5151feeec71aa1239ab0da6a94fb98d67825b3c72300d919f9cb8b8fad424a76","src/encode.rs":"94a8ea60d54ab1c69a2d684032f8b6afb3b549c84cd306e08d44d129868ba7d6","src/error.rs":"1228ce6ac8940ec0df8ca060dd711d002ea6493defa3720248b900115cdb7eec","src/index.rs":"bbc4b8f153b5232052986c9ef56c1a455175ee3ac256d2c72ace6be3069d38d4","src/inline_table.rs":"33c82c23270f87f2227ce4839d604132c8c3d005de5c4944c38c8104dfe86c44","src/internal_string.rs":"3f94a77ba6268d07146afcc341aafb2c938002c07e1ac4469e2cf61b2918a408","src/item.rs":"9c1dbb98ff4b98345d87d2ef3032492c1023d2f8aa420ae7b42fce23a32fbdec","src/key.rs":"766f5d907b74a333b585f866a4e02e44c76fdee5e25bbbfa37a4a40f05a13fd0","src/lib.rs":"b8b0c40fef9c3e8a23dd6134fa247158ab178b634da75c73fcfbf860d76ce05c","src/parser/array.rs":"d171d5092c0360f5eda9b8758752b09703ef174cf2007dde721f0108d1a07395","src/parser/datetime.rs":"ff0b4537b4b54a0b8ea9a26dd862b0e3afac153ac474fc51201f325734ddee4d","src/parser/document.rs":"c60fdea632997cebf240701401a1d9e056476dbabbfdc73d89cbdd263834a838","src/parser/error.rs":"025bce90fab50af89eaaa161f9116a577604632858a681cf8167904ddbfe354f","src/parser/inline_table.rs":"68821e3c5480f37c1ffaecb3dd82a76bcca0136376072d9a73548dddcca030b7","src/parser/key.rs":"22ffcaefe9067b27d9a012501a159fa8498870bd5102a8e80f5af5f6fdb44bcd","src/parser/mod.rs":"e3a80862bc90b32ff324d7f9103be289749671287daa43420f87e907bc2eb1bf","src/parser/numbers.rs":"1423f0e54cca7ba99b0c53fdd9df749c0bad75ce079d8941b860e5d84a5a3189","src/parser/state.rs":"2f028bf33a5afe92017f4048064941443c23b0345e7147da72e02a4d29f228b4","src/parser/strings.rs":"91fa1302dfc9aafa353c27ff4aad6fd3cc9cc2f2d9ae666c334d484bd4643167","src/parser/table.rs":"c716d82caed27229afb22e757cb14b2828356fc69d33ab94d8f43043f992cd2b","src/parser/trivia.rs":"78596aec684ab245e74e5302d6f238373c665167bc7ff21bd72923690b5e5ec2","src/parser/value.rs":"7570a1a30573e7fa144471a89a4df9ade93ab8e58e02a70f52911965d89cc2d4","src/raw_string.rs":"472573c3404d8d384728fbc92afc6e95437042b8e6499ebc4336f6f342b235d8","src/repr.rs":"eb759c28befa01db48a27208aa8a506ef136dc9701be61e56d8b5524e6317faa","src/ser/array.rs":"fdd85c61c1007d041ad42556dcf9f63bd6142dec0f82ee211cd5b9f7b4a85f3e","src/ser/key.rs":"7bdf5988b33e11f2e8f1a7f8956d10a40e36cf92791dd469ed7c10b1ed37ffcc","src/ser/map.rs":"3dfe28babf7ed4ae367e7c4cbce76cddb38af11de3a136a88434e9585d3b82b0","src/ser/mod.rs":"56463b354aeffb7549539fd2baa4f6b33814ea3ab40c721cee7d8d536c0540ff","src/ser/pretty.rs":"dba1e10ef22c0b0c6b47cd10e0d36face0ab61d5c7e50d53c878ad3f82db7805","src/ser/value.rs":"d973bad722ba3c2968f699e25bb8fe2314739f94056062e4b85ae4a72f720b03","src/table.rs":"30c1773d6e509a8e8de3712f4d48578945f9ea8c299803140d96ed6b75c89e3c","src/value.rs":"3d95e7ccd9aafa171aa8f09b4e82b33eb1241592142e4d119e57a93c98af1348","src/visit.rs":"732d9970455c62dae0523949dd9bdd16e76c05ed9de57720ea4c62d70e7efb2b","src/visit_mut.rs":"77934daa4ec1df159fc72f13431b5a635e4bbce15267a82da4647aa860a1e37f","tests/decoder.rs":"23022ffba788500248181d0cd784dca93d2b7edfaf9e560a3c379308385d401f","tests/decoder_compliance.rs":"d804ef45b0498869d05ac5c1216dc29e94963b2ec4f814e867f0fe8b71e9c639","tests/encoder.rs":"7014e99e18b9cfd34b280bc55a7e6c618269e38e5e6d483477e53e7e99459a21","tests/encoder_compliance.rs":"ca696031cb8f664728521da0370eb78e0351a5ac7134ad6cb12bc25f85757c97","tests/fixtures/invalid/array/array.stderr":"db4f1f06909589f9cb49d6657b6c39444e53b4ec8c4d3f0b9704ab5beaf68d2a","tests/fixtures/invalid/array/double-comma-1.stderr":"db4f1f06909589f9cb49d6657b6c39444e53b4ec8c4d3f0b9704ab5beaf68d2a","tests/fixtures/invalid/array/double-comma-2.stderr":"da4b8df6dbc2ef41b7b1b08b65a160ba37d07dec0c1ccf4184d6381a857b8762","tests/fixtures/invalid/array/extend-defined-aot.stderr":"a9dec506541bbdb96725d9f989572c10f29a52eb4e167fadea8929d7b6729b7b","tests/fixtures/invalid/array/extending-table.stderr":"a6c077cb49c41a9d10aca0bac6c571e570f0a772c6311d6a835f9cc46f4ab7cd","tests/fixtures/invalid/array/missing-separator-1.stderr":"98773b4d82cc32c0f8d1d8e6078109f7d9655b13d58e4b62fd22cef9f9ad725c","tests/fixtures/invalid/array/missing-separator-2.stderr":"eb38b2683f119d8b9b8c6b8ffd46da784112565ef1033febbe59469e14baea76","tests/fixtures/invalid/array/no-close-1.stderr":"a02ff4b3edf627873f0cb1f4242305f6a9a032b6f897fa13b8070c1a9ae85afe","tests/fixtures/invalid/array/no-close-2.stderr":"dfac1f12f502214388deecedf7a9720338ffd13b3184178410581e242ba8124b","tests/fixtures/invalid/array/no-close-3.stderr":"1930ad00952820805e208ebaf1ae3df243f492b6b2924bbfffbe458f31d89b29","tests/fixtures/invalid/array/no-close-4.stderr":"198293e991ab522435e383ef906f0594a0ea8126c792db934eba142369189e1c","tests/fixtures/invalid/array/no-close-5.stderr":"279aa5db5d92d3e729fe326321d2d217ff33db3c5e2bc2a5043909fc798c226e","tests/fixtures/invalid/array/no-close-6.stderr":"ccd74b7a7c01bb96a748c0e797f6207f0314d76520e4c1d1a9cde076e2284216","tests/fixtures/invalid/array/no-close-7.stderr":"9c6ab0c93fcf0c9ff7fdf8bc13f3b62864305509b80659c225a55641133abcf5","tests/fixtures/invalid/array/no-close-8.stderr":"c4b7fe79f9a354152c36e130af4862c47908cff3c84ba9be8bdbe76c35d9c6af","tests/fixtures/invalid/array/no-close-table-1.stderr":"545ad4381d5a007a6cd940a4523ae2f629617d298611c0898283a1c4783604cb","tests/fixtures/invalid/array/no-close-table-2.stderr":"5a2b4bf9026aa20c749551cd77458c5ffba008b5b235fa05fb7c756917018eb8","tests/fixtures/invalid/array/no-comma-1.stderr":"7d2f2923638cecc2f89e8ff1334a9da44fa7e137bea12d5019ef25f576f015af","tests/fixtures/invalid/array/no-comma-2.stderr":"d95a7a43534c632efa2502e3b5f8eedabf2f05ad6bfdd68e2f9fd3d8a22ba028","tests/fixtures/invalid/array/no-comma-3.stderr":"ae8ec653f564b247644854fecf59e097cc32e445aa4775aed8d18a207af5ec45","tests/fixtures/invalid/array/only-comma-1.stderr":"6f35677e711812007adc9a59341bc62bf2b7bcec80a456d19a2468ea8fd27b47","tests/fixtures/invalid/array/only-comma-2.stderr":"9d0017e6798574bd85810e64816ddfd08ccc36e25153006958fe023e2a007331","tests/fixtures/invalid/array/tables-1.stderr":"f105a34c2d87b61160881eeb09b7f54d244ba2a222d32fbfc755091939942247","tests/fixtures/invalid/array/tables-2.stderr":"77010599d1d61a34119a99acea7d84162d217df93bca01aed3ae73f1eb62dafe","tests/fixtures/invalid/array/text-after-array-entries.stderr":"391ee42f4fa3a7ec51ba1b90e69f1d9278c105426fe66ae1f80e65d7fb6ed379","tests/fixtures/invalid/array/text-before-array-separator.stderr":"7292ebcb8c9c8aaa4041279af5414de3e710977cac948988cdc8b0947223b62b","tests/fixtures/invalid/array/text-in-array.stderr":"0486e3ec5d299e39c61380a2ed8826d886edb730f6d9555a765e4314da7f5b68","tests/fixtures/invalid/bool/almost-false-with-extra.stderr":"d489307ba1d0a3dcd1bcff067437f0cde1521c6fddb1311bf396468521eabe81","tests/fixtures/invalid/bool/almost-false.stderr":"399829efe867bedbcef2ad2073df621f8297877180f71c6292209426da7b09dc","tests/fixtures/invalid/bool/almost-true-with-extra.stderr":"439c66bbdcdd335cb858f3cfc692fa3a78f7802591954909fb6807a62440d334","tests/fixtures/invalid/bool/almost-true.stderr":"3aed4e8704abc2099d8a881ee496a868c4745a0cfe7df781de613016543f4313","tests/fixtures/invalid/bool/bool.stderr":"d489307ba1d0a3dcd1bcff067437f0cde1521c6fddb1311bf396468521eabe81","tests/fixtures/invalid/bool/capitalized-false.stderr":"09c2b3b51460a52bc274c5d0b08024c76ea3dd7e067ef20765cc56d2a4f60087","tests/fixtures/invalid/bool/capitalized-true.stderr":"d0fa94dc8a4fb0f17550052fde5e10134e8ccbc84f1e90093446f1cb99f01a70","tests/fixtures/invalid/bool/just-f.stderr":"5f6a3a206bf49fb5eade446dc8f432037a4026f49beac01e453fd8c3610901b2","tests/fixtures/invalid/bool/just-t.stderr":"175bdb4ffa7259c5bc346fb088de8545e8e1d8254d1e2ac0d33c16b539e2270e","tests/fixtures/invalid/bool/mixed-case-false.stderr":"9de4d4b695614f3593abcb27a6f68ef5c578a96bec01472607c35cde28d48b10","tests/fixtures/invalid/bool/mixed-case-true.stderr":"45e314b026b819d568350bb62a39a2e5fd970acaf0a6591539d043abb9dee629","tests/fixtures/invalid/bool/mixed-case.stderr":"927b85ba3c3084e925cb8959c07d42f9f19f4ca8f84d7dd6babf15f1d9a1048f","tests/fixtures/invalid/bool/starting-same-false.stderr":"7e0f1f2d8ae538a5b156da19be0630d25e0d2dfa44bd97cdb125bf3e042df7d3","tests/fixtures/invalid/bool/starting-same-true.stderr":"bdf30c50dcc0ff8c928e753962f6dba7acc6f945a2958e0b03e690a126270ea0","tests/fixtures/invalid/bool/wrong-case-false.stderr":"811f36bc4df7755c3850b4e933e5436fb54183ac7fcf3599e8373d7545be1c2c","tests/fixtures/invalid/bool/wrong-case-true.stderr":"90520b03257419ec4a826b7f989e5aa74314a0aeeceaf6bf1873e867715495a6","tests/fixtures/invalid/control/bare-cr.stderr":"568eff61d56bb362866973d9a7a488831b8b08d02f4808538a04b22ebe10bf09","tests/fixtures/invalid/control/bare-formfeed.stderr":"86ebf66dcdf202fd832d91892b4b62eca65957198cca9567b10f3400997e9f90","tests/fixtures/invalid/control/bare-null.stderr":"e6bf2df7229c8632a1c7bce682914166520216c2d6c3ccd4c2e716dc700d5bca","tests/fixtures/invalid/control/bare-vertical-tab.stderr":"b85c55e64c58b85343482dadcd5d833fa771a79e1d0f13a24f4185a4d0d826d5","tests/fixtures/invalid/control/comment-cr.stderr":"3b334037fc70762903ebc2c3d0786554bb62d9f146dc801e07841ac7f8370e78","tests/fixtures/invalid/control/comment-del.stderr":"872065efcbcddffab0679160bbce0c8e2519ae7ccb8b4b667a8b78c4bd13522d","tests/fixtures/invalid/control/comment-ff.stderr":"bdcaf413adee168eaee3a7723b2939fe67e59914a834cbc559902eb94e4ca02b","tests/fixtures/invalid/control/comment-lf.stderr":"898787080f260e05ca20e6982ac4cce5572a6ed84a9244ce07a41f710db9a0cf","tests/fixtures/invalid/control/comment-null.stderr":"bcc48727ae370e45918682d6a17bc680b126d4792d4a33524c802f45cebc03d6","tests/fixtures/invalid/control/comment-us.stderr":"37d7a6c0a28989966af74530c2885bfc7ba6ddb31b58bce3f26543e34043b88c","tests/fixtures/invalid/control/control.stderr":"6ba75c9dbd0e03531f5a5ead2cb781a702d3600c76b2a4cf1bf7c02c1c9aca1a","tests/fixtures/invalid/control/multi-cr.stderr":"29fde9a540e77ed46dae0a227b666a9c102d5263cc5cac811e0e451bd403ad91","tests/fixtures/invalid/control/multi-del.stderr":"79f0d85f5b44a7dcad9d98adbef25b6ce54bb6dbf79ffcd3ea230a07144b4b82","tests/fixtures/invalid/control/multi-lf.stderr":"8c95b2a7b4e20dd8985e04b8da5fb9d7cbff37220a74fd3903f16f7ea7eaf39d","tests/fixtures/invalid/control/multi-null.stderr":"9fec8ad3ba45ddb96ad3b6a118b4fa648056e26a9128528b2c0a8fa3b01e741c","tests/fixtures/invalid/control/multi-us.stderr":"3a59d615cb91172936acdc42baa39e9faf3a2bb9e9078d9879a54826ffb6b20c","tests/fixtures/invalid/control/rawmulti-cd.stderr":"a71dce0ac9a79209ea173f3d656b2d663c685b47841b620cc89860fb68f0cf0b","tests/fixtures/invalid/control/rawmulti-del.stderr":"3b99e52cbec29c7b6b439bcf3cd58b85a72487e2f5af5e829dd6986b3414f49f","tests/fixtures/invalid/control/rawmulti-lf.stderr":"89fb9c7d02e39ff648cea46ec1086058016c5ef1deebc6f3b106664dc4d10eae","tests/fixtures/invalid/control/rawmulti-null.stderr":"84c04cc89a6bc716b6f7811142899014abdb0b49c4ea56bc163c19220b14c323","tests/fixtures/invalid/control/rawmulti-us.stderr":"ac53d1dcc96c3beb454f5474a23940f26c93705b76a10647c810f79facf4f6be","tests/fixtures/invalid/control/rawstring-cr.stderr":"fa8da523c3cc24384fda1dd6032607cdd380f45133ce7d6d2a1d3b8a3eb6a917","tests/fixtures/invalid/control/rawstring-del.stderr":"64378c4341fb92376e2fe8a70356411759b8659e151ed0ca744751c8b2d32155","tests/fixtures/invalid/control/rawstring-lf.stderr":"43f6431efef7ead5aa335958187d979d98dcced50cb82ccca19df34397278175","tests/fixtures/invalid/control/rawstring-null.stderr":"eafa2a63e9d12293b290405049457860a8fef70de56c4ba2f203e5f2c79a8634","tests/fixtures/invalid/control/rawstring-us.stderr":"d139a7ec7c4ff5358f6c56ea2f2e431646f5ae9bf3d927694a2aa3891637ecb0","tests/fixtures/invalid/control/string-bs.stderr":"ce6634df580f80e090749d31c178bed74b88718befea7788abe801acf4af10ae","tests/fixtures/invalid/control/string-cr.stderr":"a8dfbe00e976920a442f8f03bebeb31bbdb570a242e380b5a4c60d351614acf9","tests/fixtures/invalid/control/string-del.stderr":"bd862371b7a698647d4d68e5c3bad5c269bd8553f36d82301ffc62a4508334a4","tests/fixtures/invalid/control/string-lf.stderr":"18a1d0e1bcf6f99c80e95e94097f95722a65c2d8a415f9497b808b0bc135c12d","tests/fixtures/invalid/control/string-null.stderr":"fe34d284c478853bad94235aac1f37ac3c591f97e12990847b5da0b6c99bd47d","tests/fixtures/invalid/control/string-us.stderr":"06f7fce918057f567803f370e55e035896bb63a97eb0c0c39a680d6b927fead8","tests/fixtures/invalid/datetime/feb-29.stderr":"898cd0c4cd094247fa0825d02179d3b913cc9db4fbe634fa84c7a7d2cfb7b03e","tests/fixtures/invalid/datetime/feb-30.stderr":"0fb2c5f14857fe5edadbc93a78208c1c9ed900a18e3296613cc620e255c76e11","tests/fixtures/invalid/datetime/hour-over.stderr":"bd2220bdbaa96caf3d2aaff640620e4856bffb722a0e5be61dcb5283ffd08056","tests/fixtures/invalid/datetime/mday-over.stderr":"de9d27d65c68dd09da10c229167ce881dfe0ebda457badfe24b7598ae80c47a6","tests/fixtures/invalid/datetime/mday-under.stderr":"18daf3ce2a6a972476ccabcf92690a488e4f3be804dab8458da2aebad22a2c8c","tests/fixtures/invalid/datetime/minute-over.stderr":"a29009d3f7a6b1d9afad2420f223d6a6e02df8149577547837f5eeec4075bb9a","tests/fixtures/invalid/datetime/month-over.stderr":"37a203b22c3b2510541e413ff347447f9f3319a896ee005b96f65bc0d68150f4","tests/fixtures/invalid/datetime/month-under.stderr":"24c554595ca9a999a1d8e1ef7dc28b443f2f0ad6e17337ee157fb18bdcf678c1","tests/fixtures/invalid/datetime/no-leads-month.stderr":"2e20cb60a1ecee85b172d1402e4d8c425362e4db607706bd39494385dc6dc98e","tests/fixtures/invalid/datetime/no-leads-with-milli.stderr":"a35c496884e921aa086c1404bc812ff74f2bfd347a3ecd96640942be5555afbb","tests/fixtures/invalid/datetime/no-leads.stderr":"2e20cb60a1ecee85b172d1402e4d8c425362e4db607706bd39494385dc6dc98e","tests/fixtures/invalid/datetime/no-secs.stderr":"65871ee020e645e737c363b22cf43c160b295871cd4ac97a37d3ea46f60e3250","tests/fixtures/invalid/datetime/no-t.stderr":"ff50b85f6bc0d49000ec6f1303fda9b44bf934c2ede61743363411bbf6ebecbb","tests/fixtures/invalid/datetime/offset-overflow-hour.stderr":"c6877bc38aef57bc1163bc8e982131cdf9d9e9f2edb08a80dd1aff61db36c5e8","tests/fixtures/invalid/datetime/offset-overflow-minute.stderr":"6f5d98afaeea22cd896aa80e16abfe93745e45ad489a4575787dc378ed52f33b","tests/fixtures/invalid/datetime/second-over.stderr":"0ed555a874efa08b711b5227501208758d87a01ad8360cf76c3dc8761807fac4","tests/fixtures/invalid/datetime/time-no-leads.stderr":"b3282cb32386dd84a35468f488be5a92dd3488e951f9dd2ea39057046386b73e","tests/fixtures/invalid/datetime/y10k.stderr":"6fbe45e2032a4b8a90fef144618ef6027edc00dee219d50c8b1493b6e38586a4","tests/fixtures/invalid/encoding/bad-codepoint.stderr":"1a816a8cdd5c2c9b8ae10431d981e22c3b307e30ef3d401ab62ac1012240be44","tests/fixtures/invalid/encoding/bad-utf8-at-end.stderr":"518dc443f0404d486b40bbbd152870276016795b05f3cc8a1de64a0e08fcdda2","tests/fixtures/invalid/encoding/bad-utf8-in-comment.stderr":"e0f252d14c18ea072c098834997db8e5f68b807bb0fa6d3d34e4042a5ea6fbb7","tests/fixtures/invalid/encoding/bad-utf8-in-multiline-literal.stderr":"2328a89cd9043de10ee656f4ea0dd5e6491fd8c0484ac36099c23161dd7a2625","tests/fixtures/invalid/encoding/bad-utf8-in-multiline.stderr":"2328a89cd9043de10ee656f4ea0dd5e6491fd8c0484ac36099c23161dd7a2625","tests/fixtures/invalid/encoding/bad-utf8-in-string-literal.stderr":"eefb00fee073933fbdb95d24a9e7050c281d4719d0cb970c2c06a71a86f108b3","tests/fixtures/invalid/encoding/bad-utf8-in-string.stderr":"eefb00fee073933fbdb95d24a9e7050c281d4719d0cb970c2c06a71a86f108b3","tests/fixtures/invalid/encoding/bom-not-at-start-1.stderr":"bd4e557b8b4586cdb39a8fde46f0bb214954f9f8ef37be46e2cc19823f6d6919","tests/fixtures/invalid/encoding/bom-not-at-start-2.stderr":"27003a498cb355011782dc21f01e15457490b78c472bb9ddb54147413c8f597e","tests/fixtures/invalid/encoding/utf16-bom.stderr":"a8800edcb8f6184b712da53e74bb787c39eb891073575acbae1ad575f15043cc","tests/fixtures/invalid/encoding/utf16-comment.stderr":"edb66c01034865f484ccf7921bfcec1efaa8599762cb9cd30c9c8103275bc4e6","tests/fixtures/invalid/encoding/utf16-key.stderr":"fb6a1d4cc571ff9d1c154f0a996d61d8f260d7e505318d02c0a03b38e2414e5e","tests/fixtures/invalid/float/double-point-1.stderr":"2917901dd186adc39cb5965faf388fa2babe577ef3bfcadd4919232868a727cf","tests/fixtures/invalid/float/double-point-2.stderr":"7eda489da0436d6f0f2268aa4005b422d215b4785af0c1696c8731908a563f17","tests/fixtures/invalid/float/exp-double-e-1.stderr":"e64082e328fcfbeff57e6801448c769b12bc8e879b77421b688b2e147e386713","tests/fixtures/invalid/float/exp-double-e-2.stderr":"5c45326ef7287ea16a9e08275222e281b5d61c9322f8764f6533707f9772e255","tests/fixtures/invalid/float/exp-double-us.stderr":"ebd30aa3f7cd3a0a5e79bbbde1beff209d24f4ab58eb5552c1baf0eb2194e97b","tests/fixtures/invalid/float/exp-leading-us.stderr":"19c8f676dd45a5db09bd5baba5c3e7b661e83099a340331ee6bb10defe679569","tests/fixtures/invalid/float/exp-point-1.stderr":"23e73e4e63db888546866967a1c0319a1db269f23ee9c277b298e9f2db88800e","tests/fixtures/invalid/float/exp-point-2.stderr":"633328e085fb04d6a79cdfb696f45a1836c3a8b6afafc4cd5e16d48465aa4613","tests/fixtures/invalid/float/exp-point-3.stderr":"b8b25bdb9b28cbe695134f00fdde85b5fa79b28408831356dc3a62650159a152","tests/fixtures/invalid/float/exp-trailing-us-1.stderr":"a73b4458debd1d0f750a6c5ade21b4046dfa2fa32b7573b0dec46f9400ed4f57","tests/fixtures/invalid/float/exp-trailing-us-2.stderr":"7a62522d55afc9bcc3ce5f11a9de8a62765803a99db4114ad6a2a5b777a302ab","tests/fixtures/invalid/float/exp-trailing-us.stderr":"aaae81eba820233944bb88920621dc9c1bbd0d1a1476c0376a38d0491a30c83e","tests/fixtures/invalid/float/float.stderr":"cc664d16849deec2ae7ebee6a3f46923bd5959075e282315c4f60461cdb13a0f","tests/fixtures/invalid/float/inf-capital.stderr":"1451a322f3be80529ebc091d231b682127e783a07cfffcce67f5b9bb4455c0c3","tests/fixtures/invalid/float/inf-incomplete-1.stderr":"38cd906dfee7f13b8cbdb27f3406ab0499fae3ae16f3c77bc7fc48d009595d93","tests/fixtures/invalid/float/inf-incomplete-2.stderr":"97a9ae1ff194a95b5be2abaf2cd8179ada832cdd9fad349efa9951e7ab92e435","tests/fixtures/invalid/float/inf-incomplete-3.stderr":"034bc609343ecf1e659d6250f719e5f93512e8140228e44e57b538765e58a1f7","tests/fixtures/invalid/float/inf_underscore.stderr":"621326dde26e5364c7af1b562fb651f4184d9b5fc9bc45edc12f52b588d506bc","tests/fixtures/invalid/float/leading-point-neg.stderr":"d19e28ba2f11069800df4dd1951025aa7f75425f7258e8caf4bbf6abe0e84bc9","tests/fixtures/invalid/float/leading-point-plus.stderr":"10750e9acccb17f0682db30fb175d083d06c822a4863d3d6b8ddb6c75b7b22ec","tests/fixtures/invalid/float/leading-point.stderr":"2545b7a615528595f5d53a7338403c83a8587e70600b1501225446e5f456c805","tests/fixtures/invalid/float/leading-us.stderr":"dc958138922097b2e1e3865c7818604b2249268af4acbe5cafe0ce8c68a90a86","tests/fixtures/invalid/float/leading-zero-neg.stderr":"d1fad35fa8d18f93ebfdf681d3476f02600e5c39cc942ca9bc36181476cbbe53","tests/fixtures/invalid/float/leading-zero-plus.stderr":"ad8ba7a7c12cb4b296cc0d43915106732e6a6a713aea67034587d1fc0c8093df","tests/fixtures/invalid/float/leading-zero.stderr":"cc664d16849deec2ae7ebee6a3f46923bd5959075e282315c4f60461cdb13a0f","tests/fixtures/invalid/float/nan-capital.stderr":"d0e9234b96d4e3591ca0190a785789f9bdcaaff01a111eb57db7bc458a2dd95d","tests/fixtures/invalid/float/nan-incomplete-1.stderr":"f4bee0b1c639bf800fc4dda38276142e715cd85ab6cc5e93ae2112ea63d7de89","tests/fixtures/invalid/float/nan-incomplete-2.stderr":"dc908ec577d29083bfd709fc4bdc2fa641d7fb2ba77a5d7441215680a8839d69","tests/fixtures/invalid/float/nan-incomplete-3.stderr":"abab5a41e0f2f1bad2d2050d0c913dfd8c15e50530d53ef8de327f106f564e02","tests/fixtures/invalid/float/nan_underscore.stderr":"25b67a7d6c743f673be7b409c9990de5de8b52a1d97c32e6f4e62f33147f1872","tests/fixtures/invalid/float/trailing-point-min.stderr":"69ad03ae81990d580a6d63bdd5ab594de00c0a16694c8671704c6243b4578b38","tests/fixtures/invalid/float/trailing-point-plus.stderr":"fba0bbad890020fe943e9f23644e81bf0bb7d114230fe16182e866fddcfc108b","tests/fixtures/invalid/float/trailing-point.stderr":"2f12b368fd94304ab0126ebb5888c519475f9ca28e9ca702c477cf0085ba9216","tests/fixtures/invalid/float/trailing-us-exp-1.stderr":"217939411cc9c99589d1ef0f3919c90ca2a562c0352063aae08ba2ae53c1208b","tests/fixtures/invalid/float/trailing-us-exp-2.stderr":"ecf0002a04040c8afcae7f4bb182c0322b4d00ab88bb53405e40c7938f2a9443","tests/fixtures/invalid/float/trailing-us.stderr":"506cb8051f1045ea1dc7f11865d58cbca0216502d273e1c10366c8be7cc9ab43","tests/fixtures/invalid/float/us-after-point.stderr":"fa9fb59f703b6770be3dc094c04eb2c4add8a7a7ab79d9fe508cfeee785404f1","tests/fixtures/invalid/float/us-before-point.stderr":"14e09a7a382e249e5143d1c81d6e4623408eb2d505e1e3f86c370a3a3bf6cd9e","tests/fixtures/invalid/inline-table/bad-key-syntax.stderr":"cc3565bdd7ce5752ed2e0aa6ca10e8a414d357a1f5630d7c759f8ffb709cc540","tests/fixtures/invalid/inline-table/double-comma.stderr":"2132a1c4d97fab140089818f990284333e22ef91d20a9f65e11d4dd15b1a701a","tests/fixtures/invalid/inline-table/duplicate-key-1.stderr":"72bea73b20005f15ced977aae70a1b0f3bbe3e35598231aca9a2303d770efdc3","tests/fixtures/invalid/inline-table/duplicate-key-2.stderr":"9b69c8521345fcc21886138d9dd0f20528c71712f3de3e565087adc916113a07","tests/fixtures/invalid/inline-table/duplicate-key-3.stderr":"675b84acec95eb5778a8d280881fd83cc3f741d0c0e2a21cd74fe1ab2b6bd710","tests/fixtures/invalid/inline-table/duplicate-key-4.stderr":"5ed3af9e2011f07a04572b2bcc1dd11dd57512dd35ff616e37051a34bd1f4a94","tests/fixtures/invalid/inline-table/empty-1.stderr":"604fef40337f04e5f37a52239d6509850aba95677a7a94ca8476a6c21b648a43","tests/fixtures/invalid/inline-table/empty-2.stderr":"870f189adbefaa6f05307d5b00748f1ae1c748d96105fabeb409c3c7d59126ca","tests/fixtures/invalid/inline-table/empty-3.stderr":"766b0f61a467c8db42514016a9a3812f678b054a124361bf7b7617cf2ae073db","tests/fixtures/invalid/inline-table/linebreak-1.stderr":"45b0611d37c1ece88bf6c88b3528adc3d73e0cd3e3b24dcf07ab151023a6f488","tests/fixtures/invalid/inline-table/linebreak-2.stderr":"f7672965326b44adaf0cb4796a087fbe779a8b17fbb458090a33375d0c54e5b4","tests/fixtures/invalid/inline-table/linebreak-3.stderr":"e8c70f0c91b15e701567e93d8df1cd3bec593696af05ec1d95e8f9e00ab20fa6","tests/fixtures/invalid/inline-table/linebreak-4.stderr":"3d31147f9e1ff5f94384e4df1675dfff2da6f076cb0a729771615f05b990be91","tests/fixtures/invalid/inline-table/no-close-1.stderr":"0bcaf312d65af22a37bdd8334821d95d4212dd43905fc945ec2c8ad3d465ae7a","tests/fixtures/invalid/inline-table/no-close-2.stderr":"4c6bfd3dd94611a3bc02fc609a738bc252dc38501e7ef6ff19543d757cc564e4","tests/fixtures/invalid/inline-table/no-comma-1.stderr":"9f1c85e0df72c7e7e011c26a0d5dd9dea8b7a5e18c3ba9a53ff4a20a9429dce9","tests/fixtures/invalid/inline-table/no-comma-2.stderr":"24a06e43a94ab041395eedb94c5bdb799ed7fbf6d930791756b0e3bd4a812943","tests/fixtures/invalid/inline-table/overwrite-01.stderr":"812d1bc74d07750048a521e513a565676e606d4fa1a32d2ebda7af8fa064d3ab","tests/fixtures/invalid/inline-table/overwrite-02.stderr":"bf95d34749254300f4179ed1314cc9cabd7c7b63fc2453fc7adbc7869b63be4a","tests/fixtures/invalid/inline-table/overwrite-03.stderr":"3e2c2ce66f1e4982aab428075105a39b2e9384f2dcf6da6d715017533416e149","tests/fixtures/invalid/inline-table/overwrite-04.stderr":"8aeb424d4ccee35ae17efba8acd65aba834192672cad73e8e1e6c3fe9f878826","tests/fixtures/invalid/inline-table/overwrite-05.stderr":"ff8facd04689f13ec53ee77bc9790d25e2d2eec50f4675788d70a2bf33a85e2e","tests/fixtures/invalid/inline-table/overwrite-06.stderr":"01be3b4e31d2c0aed381fc6599d5fd78d0757e9b76e66b087e4614e98f782db3","tests/fixtures/invalid/inline-table/overwrite-07.stderr":"52bac538099395ae15c5c8786f835dbab4748be0464951a25ae0f44aeea90125","tests/fixtures/invalid/inline-table/overwrite-08.stderr":"bbbc4b8959113590e9bc27526b56bae2e1223c88493836ea0a0df4209527a038","tests/fixtures/invalid/inline-table/overwrite-09.stderr":"1b084659999a2cf01ba8feab1d9d9232a307fae584e4277210cee69f53ef6cab","tests/fixtures/invalid/inline-table/overwrite-10.stderr":"229695997044ce82d3c3979904401a44838ae70103729b6ebba468cfb1beb154","tests/fixtures/invalid/inline-table/trailing-comma.stderr":"4791911dafd6602e2891d6ffc4d32ef8e9d0c1f8f6d37e84d440feb896d9cb88","tests/fixtures/invalid/integer/capital-bin.stderr":"fcfc8b0bddd36a641d3f5cc2ceee88554619fabf6874e11cdfdd147be8781881","tests/fixtures/invalid/integer/capital-hex.stderr":"c8e2d64f9659435a0387bb7e6447896eda253fef77e0214a4073fcffbac693a7","tests/fixtures/invalid/integer/capital-oct.stderr":"ec465fa25da212b0c9b6265ac8e9cd05c1fa07d614dafb3bc9b2ca74d6c2a7a7","tests/fixtures/invalid/integer/double-sign-nex.stderr":"8d57da526240c1cf73423b688442922ae291ff26e3c09f9c3b5b150e62e5cbaa","tests/fixtures/invalid/integer/double-sign-plus.stderr":"55896d9bd19637e124482966a12109a1a8351620ddc6f8d28553d70359f523f1","tests/fixtures/invalid/integer/double-us.stderr":"f14ed7bd3ad26b2203763fa953dd6e99212e50fb8e43a4eaeb115c1a7df4fc25","tests/fixtures/invalid/integer/incomplete-bin.stderr":"64168fc7ede87a10c12f82325fce644a7d9b9c3af55a313184175df7926845e3","tests/fixtures/invalid/integer/incomplete-hex.stderr":"ed2423540e288f4673bc68822a799bea04f571db5de56154e10360b03ab79553","tests/fixtures/invalid/integer/incomplete-oct.stderr":"9ed35e3078703a38996f20dc3e86477149564c8abd237c644bdf3a5ef26e3417","tests/fixtures/invalid/integer/integer.stderr":"ed5ef991b733b3d51700364da18bf58f1b7eb68053467afcbff22775b3b82788","tests/fixtures/invalid/integer/invalid-bin.stderr":"7248d47f2c7db309254a3a41af28bc1a6e96bfa95e0c8c94d607f65a1a30cee6","tests/fixtures/invalid/integer/invalid-hex-1.stderr":"ca2af571a835ca976727c823939f7cbd6d36f7d048464ba1f8f0bc6b6558cb57","tests/fixtures/invalid/integer/invalid-hex-2.stderr":"e2970d46eadb852d34a8407929972acf7ef131c3c44978af0e6dfe205a6e993a","tests/fixtures/invalid/integer/invalid-hex.stderr":"3976255c6fe35a1e29f0fed7324eee8420ababd0f6f1f7702908c3df47c88846","tests/fixtures/invalid/integer/invalid-oct.stderr":"9f6776e33887cb446a5590d8fe4e51c36747c634cd5e4efaa84f807d3ce244e0","tests/fixtures/invalid/integer/leading-us-bin.stderr":"0cb1db77dee877423738395a720e6ebbd5a545a3b22ce710ab669b5b1f7903f5","tests/fixtures/invalid/integer/leading-us-hex.stderr":"fec78f4fe4ad481fe9ea93465c8ef5bca8b98d0bba31b48b2990870b7aa5f44b","tests/fixtures/invalid/integer/leading-us-oct.stderr":"aad69bdd80f94e907bda03558a1302e54d58d8911fe2b564e93cb0ec48403b09","tests/fixtures/invalid/integer/leading-us.stderr":"3a265cc11f1b0d43d4b532a47776486ec7c7ea7afe70813ab00c5a37cf87a9df","tests/fixtures/invalid/integer/leading-zero-1.stderr":"ed5ef991b733b3d51700364da18bf58f1b7eb68053467afcbff22775b3b82788","tests/fixtures/invalid/integer/leading-zero-2.stderr":"5c70e7874256512c0ef6bb364497d4e10154e994056f2feb7c5c729016522091","tests/fixtures/invalid/integer/leading-zero-3.stderr":"fb2730feda6f669a3b8c4332f01369e52ce1b942807f1bf3d9762b1fea04aeac","tests/fixtures/invalid/integer/leading-zero-sign-1.stderr":"c9d2d992eea36c4fe228eb74741bd8d0ede1e354cad132b79462e7b502b37f95","tests/fixtures/invalid/integer/leading-zero-sign-2.stderr":"4248329b339020cc2ea586f2775a0b4f4cbe2ae3f0f75b935263363b8be5eaf5","tests/fixtures/invalid/integer/leading-zero-sign-3.stderr":"3b414808727d3a446efdfca0033525e17536f9b54104d8a9cb9278b054d213df","tests/fixtures/invalid/integer/negative-bin.stderr":"74aae673b861bd46544e4835fe7075e20158dd69e27f75c790d48a6006476c73","tests/fixtures/invalid/integer/negative-hex.stderr":"799bd8120f4cf2c36e7f65a5f9aa43a3ec87dd95dd3bf68501059da9f21f8c9e","tests/fixtures/invalid/integer/negative-oct.stderr":"017a6a24faf9dc1cde89f62b46435f8fca493e7b61f6fbd2b6d57f0f9e80da65","tests/fixtures/invalid/integer/positive-bin.stderr":"54d8a33743737f374480cd1235bf3f7e0847d252ef7e2bb1d447529cbc0f6692","tests/fixtures/invalid/integer/positive-hex.stderr":"3b21b23cc3dd6b213a19256f4ffb4bb36172de2f739f90bbea78636f7a50524b","tests/fixtures/invalid/integer/positive-oct.stderr":"f19faef5bbb7ed8351777bdededb1c523337f2aeeec82d967c19c36069790e11","tests/fixtures/invalid/integer/text-after-integer.stderr":"07a13ad4841a452eff00947234a4ebac4d209ea0294162888db35668648bb55d","tests/fixtures/invalid/integer/trailing-us-bin.stderr":"62da06cf06527b9e9cbeba6c5299ce6001d40592e9d007c8350090977f4d1b58","tests/fixtures/invalid/integer/trailing-us-hex.stderr":"1b290eada58a7202b1a9251afd8e0e72a4caa8ad5c85036d1050e7de8141e94d","tests/fixtures/invalid/integer/trailing-us-oct.stderr":"34e6f86ffb0099e6e1ba67deb51e36af62dfce4e7299b94503a219339bf16447","tests/fixtures/invalid/integer/trailing-us.stderr":"3ab49ee921eb772f5aa4eaf0fb3619b1dcd9a9db3f4ebbd9bc505581a985e753","tests/fixtures/invalid/integer/us-after-bin.stderr":"a94a87ebab3536899ce7c0c785f020b3a236c60d24c0bd7494628ca310c40768","tests/fixtures/invalid/integer/us-after-hex.stderr":"9009b187f615f06e3392eabd8ffa58311ed1c2b1cd76f8c5bd99671242f2e026","tests/fixtures/invalid/integer/us-after-oct.stderr":"05af70a21980416fbd602337f9af22a1c600a294635d10ef1ca1b2138338e712","tests/fixtures/invalid/key/after-array.stderr":"487d957b20226ac36e27d6efb1e3d24147284c9a5e10a0188427a1c940d31ef0","tests/fixtures/invalid/key/after-table.stderr":"f70e84770817f096fcc1b6195c6b0a79d25210c6930ce412a89646040ee3d713","tests/fixtures/invalid/key/after-value.stderr":"00d4d2d3ccd61f64a92df0ca575aeafcd96e91d053d835ca855973339ba458cf","tests/fixtures/invalid/key/bare-invalid-character.stderr":"b1f64d54a43017e6cc09755fa7ba477901721d23f9271ec658fc9362f46631b3","tests/fixtures/invalid/key/dotted-redefine-table-1.stderr":"59771f7163f28a3c81209f058b7b01d616fe5022e8ee7ffb395feb44129cafea","tests/fixtures/invalid/key/dotted-redefine-table-2.stderr":"564febb355d1556df42f428a046ac6fdc5dad49b2b736be5824b0c13fcd1fae9","tests/fixtures/invalid/key/duplicate-keys-1.stderr":"73407dfd58ba687026376cc491a42bdca3b6c94a1a85ed2a6884a7fd116acee1","tests/fixtures/invalid/key/duplicate-keys-2.stderr":"7c9dfef2ef19b1487b7592a267ab5ba21c8b833dfa9ec1c3151e369c2fdba26e","tests/fixtures/invalid/key/duplicate-keys-3.stderr":"5d84bb05a826ef44134351dbace41f3d771a2d7ff3777dbff4dda8c1fe18ad62","tests/fixtures/invalid/key/duplicate-keys-4.stderr":"522ae99c93599d13c0f1acc1fdb4f3d1787e83dd379f34f07ed5cf1f8e92cbf0","tests/fixtures/invalid/key/empty.stderr":"af6d3636ca73e5496c40d9c918c59b61fd86812db262649e5268094193873130","tests/fixtures/invalid/key/end-in-escape.stderr":"86b9c28ffc74797d35a89fee58853fa85bab9638b919138ccd5d8dd524dd204c","tests/fixtures/invalid/key/escape.stderr":"155aa9389f0eb28cac3b42974af7ea9e2eef8d96f084f08f9f75e960fc8ce8c7","tests/fixtures/invalid/key/hash.stderr":"85dd91b96aa4f81cc7922b02b411f25d9053bddd1e5b893c2a2ee9d0115a7cac","tests/fixtures/invalid/key/newline-1.stderr":"714aed0a140062f977ec85b9afa50f68448c67e806168e60b4f4554ab270b2b9","tests/fixtures/invalid/key/newline-2.stderr":"8bdeb90922617a8e943e0196b929e62a7e30baebabd49870f5c31adb77ff93a6","tests/fixtures/invalid/key/newline-3.stderr":"3862b1f006b761fcc0c97166b91a20d57ba74d4da39f2590b7b5bb2493a8090b","tests/fixtures/invalid/key/newline-4.stderr":"d625f2caaf01d53d72d6f1c3df0952fe3ca8c5f3b081503cb02b9994c088b733","tests/fixtures/invalid/key/newline-5.stderr":"6d811add45be91fa4debb461126613db9c605bf758a21490db0024ed3887ea4e","tests/fixtures/invalid/key/no-eol.stderr":"440ec927e94f0e520a0f256c865041f0478e1c82f3bb79323b7ddc36fc942edf","tests/fixtures/invalid/key/open-bracket.stderr":"3b36814373f51a8ea00a448d65bc514e8d99f5163b7dd8101df62bcd0a06e801","tests/fixtures/invalid/key/partial-quoted.stderr":"dc9059a014ed53071ed170b1e280923556dc09e0be2ae96cc8474e9da59fa378","tests/fixtures/invalid/key/quoted-unclosed-1.stderr":"6cdec8a7c5352a2f246273afaa923dfa81d4d2e68cca5b4f9a19193559b164c2","tests/fixtures/invalid/key/quoted-unclosed-2.stderr":"b4817e6f85a90fbb6adf049ba57c268f9888f1b42b3d62200c359606176170b1","tests/fixtures/invalid/key/single-open-bracket.stderr":"917c0203d1e45309fcff82ce33fdd2d989f630fb99290a40cb9e08a6f7ca0ef8","tests/fixtures/invalid/key/space.stderr":"3a5fa712d667890678873e3d4e4cabb084c67091c5ec6155355d5bd4229585dc","tests/fixtures/invalid/key/special-character.stderr":"4c50f72298f2fac70ee4538438dc9ca56c33efac1eed33a370bb7d045ccfb921","tests/fixtures/invalid/key/start-bracket.stderr":"223d8a22bf34459cd9bcb993ae2a51ab3cc436674e3367e92f7d74e9f8710a45","tests/fixtures/invalid/key/start-dot.stderr":"f9366a1492ae24fd0721724b4039d2675e91219de564aff2826adefd83fac571","tests/fixtures/invalid/key/two-equals-1.stderr":"a0aae899cfa75df41104a4d3090a309fc7ebcd95bb5a944cf742f3d3fc9d4782","tests/fixtures/invalid/key/two-equals-2.stderr":"861826b9456ab3a74f63f5c555e13d959a3991dfa6ce126ae5ed14d43f7dcee1","tests/fixtures/invalid/key/two-equals-3.stderr":"71614864344e321ac5de238b7ef9d097c6d7f3ac3eee4118d96827b4b8bd6658","tests/fixtures/invalid/key/without-value-1.stderr":"16c2823a39a82c3c27e0959a691b7a95e3392d62195884697893d373b967b9c0","tests/fixtures/invalid/key/without-value-2.stderr":"d340f94f5d96f5730ab269db7ef27aca171d64e35af1181c474d75a7d11d6590","tests/fixtures/invalid/key/without-value-3.stderr":"3cf3072fe9206bfe6c682103d0414627a5a63db4c4a319cf37efeb5fe6b92007","tests/fixtures/invalid/key/without-value-4.stderr":"07132bec96e9a9a672bafdc3c448b7c596257245f8c3e2cae04641f9798644ec","tests/fixtures/invalid/key/without-value-5.stderr":"37dc02af5ab8a30161223d44ed05c99ba742a658598a1b94ff78ed09afd9b11b","tests/fixtures/invalid/key/without-value-6.stderr":"00b623259f9c58fdfbe6753978fe2a71653bed0dda5c4ce54cb2151e8f7a6a29","tests/fixtures/invalid/key/without-value-7.stderr":"5013036b7f53013f887f670c3a3ceca6358d89e6b83b27bea9aa4447fba083a4","tests/fixtures/invalid/local-date/feb-29.stderr":"49fc14bfe63430553b173e82b84fb964a8bd93eeaf8abb28ed94f92a061e0026","tests/fixtures/invalid/local-date/feb-30.stderr":"1ae91b3300919e07b64c5c5b6572be05dccba63854df52ed71a900d190a90900","tests/fixtures/invalid/local-date/mday-over.stderr":"851f565b0537d5f2d88829c62d632d7dc5841d9843a1244568ea7382d5b05857","tests/fixtures/invalid/local-date/mday-under.stderr":"34c1e1d9e5839a2a8ccfaecbf52b3f49e1a776cb17de9187c3e79cb618ac684f","tests/fixtures/invalid/local-date/month-over.stderr":"d6ec78690f874b4c99cde7e609775abdf4f00ba758216afee355f6baa2c7c010","tests/fixtures/invalid/local-date/month-under.stderr":"c6922666b726822f6ffeca857041eec16cf387f54d3b6d9c325935c1c116aa5c","tests/fixtures/invalid/local-date/no-leads-with-milli.stderr":"03ff73112eae42e69f54f80445582775309400ce7f179bd9d28043299d5da826","tests/fixtures/invalid/local-date/no-leads.stderr":"12d98e610ca2c2e04fff563f9ba2b12f5e53937df1ced544c8082fa4f64522e0","tests/fixtures/invalid/local-date/trailing-t.stderr":"87a15cd62bbe7cba2b942fe424c045ce30a12fe439a1b49587a5cc037ffa6b09","tests/fixtures/invalid/local-date/y10k.stderr":"7c5cef8b079e2e6adbb7c723a1e75f04ac1185975113e79ff6a6352e07908867","tests/fixtures/invalid/local-datetime/feb-29.stderr":"692c0e78fd9ee1e979dc32bed3ed9d3d10a4608090b4d242e379d40a9732c2ab","tests/fixtures/invalid/local-datetime/feb-30.stderr":"330e20a8e3d95195227c3e66861f013b70fc02010107c9d0a11085bd533b49cc","tests/fixtures/invalid/local-datetime/hour-over.stderr":"a095bf7be4a76d0c9362d5e4398d5783aa1545ec03f910a2c6a1709a3f60225c","tests/fixtures/invalid/local-datetime/mday-over.stderr":"cd707915cf15690dcc65868408f115bc90da19beae1fbd0a7fce8d02ce76300d","tests/fixtures/invalid/local-datetime/mday-under.stderr":"fd8ac4e2804d96d6b7f63eb1616096bdcc3ecc2b74d75c3fc4bd98f2f3af205b","tests/fixtures/invalid/local-datetime/minute-over.stderr":"95fef6b0821ffe465ea5ce27a4f1521a4309cc64ee03e2781e63ead3003cfc62","tests/fixtures/invalid/local-datetime/month-over.stderr":"f739e1cb0538ab5e3cd0923165d54bebe10a6cee1cd773129230d508af946901","tests/fixtures/invalid/local-datetime/month-under.stderr":"cc1749c4aaa0cec5e88a2cae52bf84da2e862b6d2dae2c46615eafa0b7f370bc","tests/fixtures/invalid/local-datetime/no-leads-with-milli.stderr":"d1d95c88dcbde166b3a89c562e87bb9dc7c7d4efb5aaf999f0c253b1109ffd2a","tests/fixtures/invalid/local-datetime/no-leads.stderr":"8be255d1f994769e4cb9fdc6b4b6d65ad2664348026e409026421be423150076","tests/fixtures/invalid/local-datetime/no-secs.stderr":"019be93baccb4283a186cfb846b8cd4c848a373319daf4955ab3bec451a7b730","tests/fixtures/invalid/local-datetime/no-t.stderr":"eca57151e4e310f22620f243582486fb3e5ade13e4d4a5fb4582252fd915ca04","tests/fixtures/invalid/local-datetime/second-over.stderr":"dad38092a29679601af7ae3b05960068fa0eec5a649858ab88aedaada6ffff00","tests/fixtures/invalid/local-datetime/time-no-leads.stderr":"b3282cb32386dd84a35468f488be5a92dd3488e951f9dd2ea39057046386b73e","tests/fixtures/invalid/local-datetime/y10k.stderr":"ebb06f456d5e909af5f41fffa29c3f42eea70d91a97769716da5c75de9f75ac0","tests/fixtures/invalid/local-time/hour-over.stderr":"e5aefc4baa094437e16b5a78c89df8469ac59d899433e1c0fcbf433344b3a4f9","tests/fixtures/invalid/local-time/minute-over.stderr":"b66ec93e63f27d0a4e63c0eb1fc2d3f8dbf2fc916e213192852b1b7ac0e21aa7","tests/fixtures/invalid/local-time/no-secs.stderr":"d9939a2e05d15375d4936c6ad7c4dbd13085e638573094f3a0befd9027c0a3ab","tests/fixtures/invalid/local-time/second-over.stderr":"f14b188fb85d8818ab953f2fa0d0b8de906dddc2f0e0ccf5ef68b3e81d619f20","tests/fixtures/invalid/local-time/time-no-leads-2.stderr":"f725d49ddb5af69b7285f071d68e3d8441d5e331adcfd8c025c1f3cbb68028a5","tests/fixtures/invalid/local-time/time-no-leads.stderr":"cf06f3847f3d14655a94d8cfd5a6984dc74115b1d3cdbee0662ef215738bbf65","tests/fixtures/invalid/spec/inline-table-2-0.stderr":"5ad1a938b1d1f0f3fdbd1871efdebfd30e136407ecdd9e2eff22150d00624b3f","tests/fixtures/invalid/spec/inline-table-3-0.stderr":"fcbc05e911b7db81bd918768fe98a51a7026fd476d616718cc417d2f08bcc1a1","tests/fixtures/invalid/spec/key-value-pair-1.stderr":"d5391142dfd56040840cf91b1e28e3c048229e3d9998534d41001cd6657f9bd6","tests/fixtures/invalid/spec/keys-2.stderr":"3c4ee6066fc75d2c1f1b325f618a01113694c318e330ff4f237e89127f332c87","tests/fixtures/invalid/spec/string-4-0.stderr":"910ee4b240159b828a7509c8dfb46507071a8d8636f3935a3914d6d91f315295","tests/fixtures/invalid/spec/string-7-0.stderr":"5128f0a930b3034e494a6bee4f384a587e9fd858b25f8cc529a488c94ee9670d","tests/fixtures/invalid/spec/table-9-0.stderr":"49dac70d337266f5c6b333fee468f279fed1bff62bfb4ec7436c8b6683ce0dd2","tests/fixtures/invalid/spec/table-9-1.stderr":"d9e071c70356c01b6537f876989ad2067e7773dd5eb24a298439d192dbad12d0","tests/fixtures/invalid/string/bad-byte-escape.stderr":"14f6ae446b3b8cb434267eba11c6ec5a1badef4f867169b173698cf9f1a29d95","tests/fixtures/invalid/string/bad-concat.stderr":"499219633467b9174471db40543ca188e2b906c470e511d2f701f5f5475d96be","tests/fixtures/invalid/string/bad-escape-1.stderr":"34a15ce7012217c62d31d5392038517c216f0cbfd5d75fb5f3c2bb07afd3f25c","tests/fixtures/invalid/string/bad-escape-2.stderr":"955aab40b16043c847d85d04e6adcd093c930dd8416d29c2ab5953c077eac6f4","tests/fixtures/invalid/string/bad-escape-3.stderr":"ef8302d7a6f9b8beb54478756a6069dfafc203f640a4afa2a58fbf13fdb35b8b","tests/fixtures/invalid/string/bad-hex-esc-1.stderr":"aea935cf1e17743356e6fb1059afed2d0ee5262906594782e5537a025398038e","tests/fixtures/invalid/string/bad-hex-esc-2.stderr":"deac5217cf80acc759e1b40c43f5f56431b276dc2c896aae5490d57583105e06","tests/fixtures/invalid/string/bad-hex-esc-3.stderr":"94ecf886427e8fe5daf1d8f932bf1887f2533b10bc1f57cb6de03ea28fef466f","tests/fixtures/invalid/string/bad-hex-esc-4.stderr":"382b011dd4070554ee875fde06703d8332ef6ad36f3619f3536b0a4997ee2745","tests/fixtures/invalid/string/bad-hex-esc-5.stderr":"a8a039fae822eda68591da28ff2a117b5d85e99d066e9126ebbb6426a1cad52d","tests/fixtures/invalid/string/bad-multiline.stderr":"141e5770190dd184bb1f64f6bb14fc017210bbd918ab5c8b7a3d80b86b21772b","tests/fixtures/invalid/string/bad-slash-escape.stderr":"d62f894ee166bddf84432507fb4ba56473c0a230fd88a3ccc2b199a72a34e613","tests/fixtures/invalid/string/bad-uni-esc-1.stderr":"b7d8a7f41600a6fc5cef5fd938fab31e1a516b5075bb5f6b22ee77e49bbeb195","tests/fixtures/invalid/string/bad-uni-esc-2.stderr":"5c23cec7a912ccba77180889e44dd84287fbbfdb170367146e9d633637124052","tests/fixtures/invalid/string/bad-uni-esc-3.stderr":"744574793d570b012ee2aa537405af14612183b769e425a04bd0c0ec6e14da7c","tests/fixtures/invalid/string/bad-uni-esc-4.stderr":"16543872b51db7ff6a87cdf1ae71917857e5118b90ceb3e0835525c9bd67d02d","tests/fixtures/invalid/string/bad-uni-esc-5.stderr":"f0ef02d2988680da67942d8599e7753f2e6c89a984643000a67ebf4c34722374","tests/fixtures/invalid/string/bad-uni-esc-6.stderr":"eff7b6dd907132aa9598dca52bf12d48be066a6a8d426ce95d5f4b344bfe8d98","tests/fixtures/invalid/string/bad-uni-esc-7.stderr":"24cd2f58919584c4dd12f0262933c8c0c6142a2b62c747d465ca1b9b4986093c","tests/fixtures/invalid/string/basic-byte-escapes.stderr":"b42fd0273c7438bf13ddea9552204bb9209cdcc8e4151311d2446185d2cd546a","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-1.stderr":"725cd4955987c3d6e736832281316d6c1a2446303e9a1dc78900cef4bb84ee64","tests/fixtures/invalid/string/basic-multiline-out-of-range-unicode-escape-2.stderr":"c6698fbdb95188d53bfdaa4a4f590d86a73aafcc321a5d9511ab43ce51be1c78","tests/fixtures/invalid/string/basic-multiline-quotes.stderr":"28177a49532f22aaffc9dc204592a2c5eca2fc20f8e208b7c7f589201e8b7de5","tests/fixtures/invalid/string/basic-multiline-unknown-escape.stderr":"a83406b30eb3ab2cebb0230d8d65d0b7583885138f2c070976ae61de2c8b17f3","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-1.stderr":"19af67599c6c2eef340c9fdb0ab2cc788928def50280af939247a1274447781f","tests/fixtures/invalid/string/basic-out-of-range-unicode-escape-2.stderr":"0e2e1a69358502ec17a07e4fc151b70e8a3b5123798cb38f98fe2d146515a84e","tests/fixtures/invalid/string/basic-unknown-escape.stderr":"1de467948fb18f61336350063701d9c5a6615054fe740a9be650f71f5ca4236b","tests/fixtures/invalid/string/literal-multiline-quotes-1.stderr":"249123229606aa8eedff1b5bdead5022daf470e47dbca639e32019d1d61dbcf9","tests/fixtures/invalid/string/literal-multiline-quotes-2.stderr":"d9784af1ff056a90bf531307749d53a5d24ffffbc0f4aada7fcee417a50d1615","tests/fixtures/invalid/string/missing-quotes.stderr":"462f24701d2c51d36b18d06b69be2f6eb36449b5f3ffbaa737fcbd2b2151ae4a","tests/fixtures/invalid/string/multiline-bad-escape-1.stderr":"18469c4d37d011b3f30ae17e3111b5e8a9526d593475e5d8d7a9b19461a40e8d","tests/fixtures/invalid/string/multiline-bad-escape-2.stderr":"d43896d3005c8470dc8149e2b74eb8825c6d9fedfe9f48125ad88a95c1dc3035","tests/fixtures/invalid/string/multiline-bad-escape-3.stderr":"92f732c6bcb922e25d2a001a389f93b596dd0e91109cbdcb651efa146309dc2a","tests/fixtures/invalid/string/multiline-bad-escape-4.stderr":"5e549045d08a76406f29c10433766f1c32aa69a169f3dc138103b4615c9979b1","tests/fixtures/invalid/string/multiline-escape-space-1.stderr":"94b451b6c03055186777a248cb216f95a1b2e29df25549f345d96bd0a4e63f1e","tests/fixtures/invalid/string/multiline-escape-space-2.stderr":"850d75a72e44e1ceeced7d65b810afa5b2935f3ab3efea0f3e147a06d82a6637","tests/fixtures/invalid/string/multiline-lit-no-close-1.stderr":"8e81ec582e75388024196cb5a64aaccc128c874be341fba01973c722e64a0d1b","tests/fixtures/invalid/string/multiline-lit-no-close-2.stderr":"9572aa98be504bab0172f612c40010d005577a6cd30f7fb3d25ea29c73e0489a","tests/fixtures/invalid/string/multiline-lit-no-close-3.stderr":"cc89579aa2201fc3bbe98693f822516557d950a9844e68ace4355991dd02478b","tests/fixtures/invalid/string/multiline-lit-no-close-4.stderr":"2310b339cc17b8aefce9ef0e2805c77d5cce3d70abe93373ebb8f986169afe00","tests/fixtures/invalid/string/multiline-no-close-1.stderr":"d5b9602d23b0cb023fbe3ae80d862fd60332475ba8863a1e977f17cb326a4548","tests/fixtures/invalid/string/multiline-no-close-2.stderr":"e500e99a44305b1e148b211e963478cf1554f8c9536d3108390cf41d5b2ce069","tests/fixtures/invalid/string/multiline-no-close-3.stderr":"efea30ada8d63f3071be499d63a82729b8f2c3d5f6155a1dca456e7d790f84be","tests/fixtures/invalid/string/multiline-no-close-4.stderr":"30ce703d6a38d97861b37d8539648734145e6ce45c39b2dc8b970b7b34557031","tests/fixtures/invalid/string/multiline-no-close-5.stderr":"a8b6610cb194104520cdc9fdd140192b6b1c738a3fe922a9f40c21b91f82487e","tests/fixtures/invalid/string/multiline-quotes-1.stderr":"046956658c0a73e665e7a6a2044ff83c8efb8cdd8c2ab153c163eb1e61068c56","tests/fixtures/invalid/string/no-close-1.stderr":"3ad8aff0932d98592b808fc6f44fa68a854097f8025e92c11af1acb6de3d3cc7","tests/fixtures/invalid/string/no-close-2.stderr":"3438330fa63c592f316342c9786696a2426df2838d60ee52889a9dc2527ce77c","tests/fixtures/invalid/string/no-close-3.stderr":"06e62924f3e7a0d290ab2a377e6a6b96c69bafee7170feb37331d6220e956e38","tests/fixtures/invalid/string/no-close-4.stderr":"e5cacd943fec6e10430242c95b42cdd55068cc053f5b40eff38e077a70d0109a","tests/fixtures/invalid/string/string.stderr":"aea935cf1e17743356e6fb1059afed2d0ee5262906594782e5537a025398038e","tests/fixtures/invalid/string/text-after-string.stderr":"1c1e4677be8d3dba0e7933b3ed1cbb6e0bcf6f600cf9a989a7b09c9424a4d0a7","tests/fixtures/invalid/string/wrong-close.stderr":"441f4f1b73c11c8dbf2f73cf9a7766f17a9517b3b9142e86736ed43eaec07f18","tests/fixtures/invalid/table/append-to-array-with-dotted-keys.stderr":"e16e10a17e38898bfff5b16887d9143fa5036669e83b75e84aa4ba4078b1a9a8","tests/fixtures/invalid/table/append-with-dotted-keys-1.stderr":"a67f1f152005295e0a1bb3dcaaa755edd05f19ac5316b8ad2eb4d45797e0f770","tests/fixtures/invalid/table/append-with-dotted-keys-2.stderr":"72d9ea8a90b4d9e5319c2bf951bdde6a87a205612e82ed5a09cea2b706bfde7f","tests/fixtures/invalid/table/array-empty.stderr":"e8a41c60adf7756361920816b6c4f44125a813c869b71fae2c98473e4da1b231","tests/fixtures/invalid/table/array-implicit.stderr":"7797ce41aab0567fc9d40e277cc32c12e1f16ffc0e73857fdb3bbf754246305f","tests/fixtures/invalid/table/array-no-close-1.stderr":"5f1e8703d59398f6595d21ed0abcc7dc3ce77943ad0f71eede9ad63ea2bcc7c1","tests/fixtures/invalid/table/array-no-close-2.stderr":"5adeffef5a1e1d63b6461f2a734a5b557bd3709e4fde903262be0452890623a6","tests/fixtures/invalid/table/duplicate-key-dotted-array.stderr":"9158eaf24fd4237fb87a6fb9be00e18ea935cb509a657bfe370cfa769e97cef6","tests/fixtures/invalid/table/duplicate-key-dotted-table.stderr":"ca58908463cbe2ec6b3de314237c178fee64245cc738c72a7b9e08bb3d02b2b0","tests/fixtures/invalid/table/duplicate-key-dotted-table2.stderr":"cb59f2ed324642de947f3cd9373ca111ec35104a5f33578f64c48084ce1a84f5","tests/fixtures/invalid/table/duplicate-key-table.stderr":"f4816522738b3e2ace87d1100a3d73e6a122d8dc67d05e0b35a1438e16a8952c","tests/fixtures/invalid/table/duplicate-table-array.stderr":"11d293e4b4f205fc98cd892f25a25f533cb922c963ecf095a932d2e9d550be4f","tests/fixtures/invalid/table/duplicate-table-array2.stderr":"fa9cd3b1212eed14ec56b66a16471ac2f7c0398d743982abb7c5cb4b5c7a5fe4","tests/fixtures/invalid/table/duplicate.stderr":"3e6d1b1a2f44d449e8cb0098e7c40ad1e755363b446f3821c399abfb26eb9939","tests/fixtures/invalid/table/empty-implicit-table.stderr":"cd3606ce97c5537d18146cd978403636a65fa703c83616da75b8cafa86e8fa24","tests/fixtures/invalid/table/empty.stderr":"4399e419abbcfbec93f5915e7fbdd11b6e462a4c066a29eacda159abfc588734","tests/fixtures/invalid/table/equals-sign.stderr":"472de6b908a03c99637b635a3a898ed956684ae422e1b4b135ec94986ea45f2d","tests/fixtures/invalid/table/llbrace.stderr":"db6bbee7ed15994398901c46ed4b40904897e71f5d972deb7904ccac49cd834e","tests/fixtures/invalid/table/nested-brackets-close.stderr":"e1dff60ea8f77dd1b8fae7d1d63c788c838c80560172d92377cc168f5cb5923a","tests/fixtures/invalid/table/nested-brackets-open.stderr":"bd58eb0630dc0c51ebc288258d360d707c8f43a5877ddc21e9420f8eb76a2f4c","tests/fixtures/invalid/table/no-close-1.stderr":"ae6326db737d2e259c051fbe3f5aa6ef7d7ec1bd47930ea78e09667a20100a72","tests/fixtures/invalid/table/no-close-2.stderr":"4b26e3e14f79c37d8e4e4c6ca69ae62868b3f2f249fb8f57abdcb6b8132a553e","tests/fixtures/invalid/table/no-close-3.stderr":"6bf7e2d30c735a55f595140af7c7f6be89b6faf868f4473ea39570fdb87d5823","tests/fixtures/invalid/table/no-close-4.stderr":"917c0203d1e45309fcff82ce33fdd2d989f630fb99290a40cb9e08a6f7ca0ef8","tests/fixtures/invalid/table/no-close-5.stderr":"c1a691a6fa9638b75010f37166c29a6e5a2da2e35bd9a321118d7ea384af2d77","tests/fixtures/invalid/table/overwrite-array-in-parent.stderr":"300782a740fff829dfe485a4a43426a53f82bb6afca63ef82fc07081c43d8707","tests/fixtures/invalid/table/overwrite-bool-with-array.stderr":"dcd33263a49a91ed583c3f53c6e86d6c5b8d493d841aea074a5a81f57cb5c152","tests/fixtures/invalid/table/overwrite-with-deep-table.stderr":"b128988d3a37f5857c41751847ed0d9590e4cbda66a55238f73c60d992749e41","tests/fixtures/invalid/table/redefine-1.stderr":"3e794bce5bb6ae9f603f50e3dc62d136701ec478078e8a8e99c94229778e24ca","tests/fixtures/invalid/table/redefine-2.stderr":"76a6fa1ea8d5da8a78aecb88c506dcf4e07906984baa8c9d1a363b2bebfa4281","tests/fixtures/invalid/table/redefine-3.stderr":"6ebf320d6d2117c189dd8d425303a66739a4813e4abef2d3184dc9ef5915d959","tests/fixtures/invalid/table/rrbrace.stderr":"342a5ff362c8b4c1e85a6442029291bd33165a3b36552794fcd5269249bf36a1","tests/fixtures/invalid/table/super-twice.stderr":"78b95be29fe54e92ebc82d8b35e0d59f5b9796f323b7434a49daf72d0f0be02e","tests/fixtures/invalid/table/text-after-table.stderr":"6dfaf1fc3199f0602fea52f7b1c65869eb2f8643b9e90dc1e718a183fb972485","tests/fixtures/invalid/table/whitespace.stderr":"fa48d4dc83f92e729dc25c6fc6a0c336014391b4bdb3392998f18141d2deb350","tests/fixtures/invalid/table/with-pound.stderr":"97dbd1ceb7f357bd98cc1caa9a602c638aaa5831237b7d63b18153acc64d3af4","tests/invalid.rs":"3fc6ee10d5fbe44bfa105a52909955637b3ce8d587f23994ff8a47636680676f","tests/testsuite/convert.rs":"f27d5a28ae0bb918293d7542c2b2928cc9b5f2eae4a6909479f24e05ba4d63f6","tests/testsuite/datetime.rs":"6033aa2c78c5c71bd7025ad323ad48d1cde158fb27ae890136f3d0f4e52fb1dd","tests/testsuite/edit.rs":"59e1344044112396335c71cbf800be15e0ac993c0837036830e420a5cae00aef","tests/testsuite/float.rs":"4a572ec1ec6ed66c8010081dc550ec3d1bf24666fa787883b4f2f541b576c91d","tests/testsuite/invalid.rs":"90dbd8e2b14fdb571c8445f2c49973006dd9096ed3ce3caca40b1cf5947a980b","tests/testsuite/main.rs":"b78ad4077facdf0e31ef77355fb3deb70d8339befbdb9ef16abca3b05231556e","tests/testsuite/parse.rs":"3250ba996befd939ee34c8e1fb89812dd3a83bebc933ff4ba7dcdac883bf36dd","tests/testsuite/stackoverflow.rs":"4cf1fdb26c3fd4e88eed5f6d49e8be866e56f43f4f8bc85d547f9630391272e9"},"package":"583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"}
\ No newline at end of file
diff --git a/crates/toml_edit/Android.bp b/crates/toml_edit/Android.bp
index ad28523..57335f5 100644
--- a/crates/toml_edit/Android.bp
+++ b/crates/toml_edit/Android.bp
@@ -17,7 +17,7 @@
     name: "libtoml_edit",
     crate_name: "toml_edit",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.22.4",
+    cargo_pkg_version: "0.22.20",
     crate_root: "src/lib.rs",
     edition: "2021",
     features: [
diff --git a/crates/toml_edit/Cargo.lock b/crates/toml_edit/Cargo.lock
index 15a049c..4e2e80e 100644
--- a/crates/toml_edit/Cargo.lock
+++ b/crates/toml_edit/Cargo.lock
@@ -4,88 +4,123 @@
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.19"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "anstream"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
 dependencies = [
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
- "anstyle-wincon",
+ "anstyle-wincon 1.0.2",
  "colorchoice",
  "is-terminal",
  "utf8parse",
 ]
 
 [[package]]
-name = "anstyle"
-version = "1.0.0"
+name = "anstream"
+version = "0.6.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon 3.0.3",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.0"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.0.0"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
 dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
 dependencies = [
  "anstyle",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
-name = "autocfg"
-version = "1.1.0"
+name = "anstyle-wincon"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bstr"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
 dependencies = [
- "memchr",
+ "anstyle",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
-name = "cc"
-version = "1.0.73"
+name = "autocfg"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "serde",
+]
 
 [[package]]
 name = "cfg-if"
@@ -95,92 +130,74 @@
 
 [[package]]
 name = "chrono"
-version = "0.4.22"
+version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
 dependencies = [
- "num-integer",
  "num-traits",
 ]
 
 [[package]]
 name = "clap"
-version = "4.0.32"
+version = "4.3.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
+checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487"
 dependencies = [
- "bitflags",
+ "clap_builder",
  "clap_derive",
- "clap_lex",
- "is-terminal",
  "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e"
+dependencies = [
+ "anstream 0.3.2",
+ "anstyle",
+ "clap_lex",
  "strsim",
- "termcolor",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.0.21"
+version = "4.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
 dependencies = [
  "heck",
- "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 1.0.105",
+ "syn",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.3.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
-dependencies = [
- "os_str_bytes",
-]
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
 
 [[package]]
 name = "equivalent"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.1"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
 dependencies = [
- "errno-dragonfly",
  "libc",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-dependencies = [
- "cc",
- "libc",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -193,58 +210,65 @@
 ]
 
 [[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
-name = "globset"
-version = "0.4.9"
+name = "getrandom"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "globset"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
 dependencies = [
  "aho-corasick",
  "bstr",
- "fnv",
  "log",
- "regex",
+ "regex-automata",
+ "regex-syntax",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.14.0"
+version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 
 [[package]]
 name = "heck"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
 name = "ignore"
-version = "0.4.18"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
+checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
 dependencies = [
- "crossbeam-utils",
  "globset",
  "lazy_static",
  "log",
@@ -277,41 +301,39 @@
 
 [[package]]
 name = "indexmap"
-version = "2.0.0"
+version = "2.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 dependencies = [
  "equivalent",
  "hashbrown",
 ]
 
 [[package]]
-name = "io-lifetimes"
-version = "1.0.3"
+name = "is-terminal"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
+checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
 dependencies = [
+ "hermit-abi",
  "libc",
- "windows-sys 0.42.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
-name = "is-terminal"
-version = "0.4.7"
+name = "is_terminal_polyfill"
+version = "1.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+checksum = "b52b2de84ed0341893ce61ca1af04fa54eea0a764ecc38c6855cc5db84dc1927"
 dependencies = [
- "hermit-abi 0.3.1",
- "io-lifetimes",
- "rustix",
- "windows-sys 0.48.0",
+ "is-terminal",
 ]
 
 [[package]]
 name = "itoa"
-version = "1.0.3"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "kstring"
@@ -330,26 +352,21 @@
 
 [[package]]
 name = "libc"
-version = "0.2.142"
+version = "0.2.154"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
 [[package]]
 name = "libtest-mimic"
-version = "0.6.0"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7b603516767d1ab23d0de09d023e62966c3322f7148297c35cf3d97aa8b37fa"
-dependencies = [
- "clap",
- "termcolor",
- "threadpool",
-]
-
-[[package]]
-name = "libtest-mimic"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0f4c6f44ecfd52e8b443f2ad18f2b996540135771561283c2352ce56a1c70b"
+checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58"
 dependencies = [
  "clap",
  "escape8259",
@@ -359,24 +376,21 @@
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.3.6"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
 name = "log"
-version = "0.4.17"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
 name = "memchr"
-version = "2.5.0"
+version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
 [[package]]
 name = "normalize-line-endings"
@@ -385,93 +399,137 @@
 checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
 
 [[package]]
-name = "num-integer"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
 name = "num-traits"
-version = "0.2.15"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
+ "libm",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.13.1"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi",
  "libc",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.15.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
-name = "os_str_bytes"
-version = "6.3.0"
+name = "ppv-lite86"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
-
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn 1.0.105",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.67"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
+checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
-name = "quote"
-version = "1.0.33"
+name = "proptest"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags",
+ "lazy_static",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
-name = "regex"
-version = "1.6.0"
+name = "rand"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -480,35 +538,46 @@
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.27"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
 
 [[package]]
 name = "rustix"
-version = "0.37.7"
+version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
  "bitflags",
  "errno",
- "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.45.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "rustversion"
-version = "1.0.14"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
 
 [[package]]
 name = "ryu"
-version = "1.0.15"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "same-file"
@@ -521,29 +590,29 @@
 
 [[package]]
 name = "serde"
-version = "1.0.193"
+version = "1.0.203"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.193"
+version = "1.0.203"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.41",
+ "syn",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.96"
+version = "1.0.119"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0"
 dependencies = [
  "itoa",
  "ryu",
@@ -552,29 +621,27 @@
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.5"
+version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "similar"
-version = "2.2.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
+checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
 
 [[package]]
 name = "snapbox"
-version = "0.4.11"
+version = "0.6.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835"
+checksum = "40e14d10e4c2b4331ac24c33baa5a03e1fbca81c045b285b53b2a612d28569fb"
 dependencies = [
- "anstream",
+ "anstream 0.6.14",
  "anstyle",
- "ignore",
- "libtest-mimic 0.6.0",
  "normalize-line-endings",
  "similar",
  "snapbox-macros",
@@ -582,11 +649,11 @@
 
 [[package]]
 name = "snapbox-macros"
-version = "0.3.4"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31"
+checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d"
 dependencies = [
- "anstream",
+ "anstream 0.6.14",
 ]
 
 [[package]]
@@ -603,9 +670,9 @@
 
 [[package]]
 name = "syn"
-version = "1.0.105"
+version = "2.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
+checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -613,31 +680,33 @@
 ]
 
 [[package]]
-name = "syn"
-version = "2.0.41"
+name = "tempfile"
+version = "3.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
 dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "termcolor"
-version = "1.1.3"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
 name = "thread_local"
-version = "1.1.4"
+version = "1.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
 dependencies = [
+ "cfg-if",
  "once_cell",
 ]
 
@@ -652,9 +721,9 @@
 
 [[package]]
 name = "toml-test"
-version = "1.0.0"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec3892835fb31e181a87e1758275a64b0d7c6c9e9618aeb61a647bd487314c0"
+checksum = "f9e26681e9154ffb40044019b6bb374f6ed7fef1e367d3d314f0daf2b00faba9"
 dependencies = [
  "chrono",
  "ryu",
@@ -664,41 +733,42 @@
 
 [[package]]
 name = "toml-test-data"
-version = "1.8.0"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6b5bad99e813ce8c67d1d67c9b9f37c8451933f45eae0ab2b3583975f1cc15d"
+checksum = "13bb6bf962107303ade738a8f729f4f92c29b2d84c0772cc376f7001602afa1a"
 dependencies = [
  "include_dir",
 ]
 
 [[package]]
 name = "toml-test-harness"
-version = "0.4.8"
+version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1be4b8d761dee51b4694e9f1d622a1d7f9c135a8b8265459e16d09ac5b16a05d"
+checksum = "0ad65271b9325d4727b1afb346e2eb4cade8e998797682da4e73b7b6d902f2b2"
 dependencies = [
  "ignore",
- "libtest-mimic 0.6.0",
+ "libtest-mimic",
  "toml-test",
  "toml-test-data",
 ]
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.5"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.4"
+version = "0.22.20"
 dependencies = [
  "indexmap",
  "kstring",
- "libtest-mimic 0.7.0",
+ "libtest-mimic",
+ "proptest",
  "serde",
  "serde_json",
  "serde_spanned",
@@ -710,10 +780,16 @@
 ]
 
 [[package]]
-name = "unicode-ident"
-version = "1.0.4"
+name = "unarray"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "utf8parse"
@@ -722,75 +798,37 @@
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
-name = "version_check"
-version = "0.9.4"
+name = "wait-timeout"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "walkdir"
-version = "2.3.2"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
 dependencies = [
  "same-file",
- "winapi",
  "winapi-util",
 ]
 
 [[package]]
-name = "winapi"
-version = "0.3.9"
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.5"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
 dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows-sys"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.1",
- "windows_aarch64_msvc 0.42.1",
- "windows_i686_gnu 0.42.1",
- "windows_i686_msvc 0.42.1",
- "windows_x86_64_gnu 0.42.1",
- "windows_x86_64_gnullvm 0.42.1",
- "windows_x86_64_msvc 0.42.1",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.1",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -799,128 +837,144 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
 name = "windows-targets"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm 0.42.1",
- "windows_aarch64_msvc 0.42.1",
- "windows_i686_gnu 0.42.1",
- "windows_i686_msvc 0.42.1",
- "windows_x86_64_gnu 0.42.1",
- "windows_x86_64_gnullvm 0.42.1",
- "windows_x86_64_msvc 0.42.1",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
 ]
 
 [[package]]
 name = "windows-targets"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
 dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.42.1"
+version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.52.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
 
 [[package]]
 name = "winnow"
-version = "0.5.0"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7"
+checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
 dependencies = [
  "memchr",
 ]
diff --git a/crates/toml_edit/Cargo.toml b/crates/toml_edit/Cargo.toml
index 2e27069..ac59843 100644
--- a/crates/toml_edit/Cargo.toml
+++ b/crates/toml_edit/Cargo.toml
@@ -11,13 +11,14 @@
 
 [package]
 edition = "2021"
-rust-version = "1.69"
+rust-version = "1.65"
 name = "toml_edit"
-version = "0.22.4"
+version = "0.22.20"
 authors = [
     "Andronik Ordian <[email protected]>",
     "Ed Page <[email protected]>",
 ]
+build = false
 include = [
     "build.rs",
     "src/**/*",
@@ -29,7 +30,10 @@
     "examples/**/*",
     "tests/**/*",
 ]
+autobins = false
+autoexamples = false
 autotests = false
+autobenches = false
 description = "Yet another format-preserving TOML parser."
 readme = "README.md"
 keywords = [
@@ -90,8 +94,13 @@
 [Unreleased]: https://github.com/toml-rs/toml/compare/{{tag_name}}...HEAD"""
 search = "<!-- next-url -->"
 
+[lib]
+name = "toml_edit"
+path = "src/lib.rs"
+
 [[example]]
 name = "visit"
+path = "examples/visit.rs"
 test = true
 required-features = [
     "parse",
@@ -99,19 +108,14 @@
 ]
 
 [[test]]
-name = "testsuite"
-required-features = [
-    "parse",
-    "display",
-]
-
-[[test]]
 name = "decoder_compliance"
+path = "tests/decoder_compliance.rs"
 harness = false
 required-features = ["parse"]
 
 [[test]]
 name = "encoder_compliance"
+path = "tests/encoder_compliance.rs"
 harness = false
 required-features = [
     "parse",
@@ -120,8 +124,20 @@
 
 [[test]]
 name = "invalid"
+path = "tests/invalid.rs"
 harness = false
-required-features = ["parse"]
+required-features = [
+    "parse",
+    "display",
+]
+
+[[test]]
+name = "testsuite"
+path = "tests/testsuite/main.rs"
+required-features = [
+    "parse",
+    "display",
+]
 
 [dependencies.indexmap]
 version = "2.0.0"
@@ -137,29 +153,31 @@
 optional = true
 
 [dependencies.serde_spanned]
-version = "0.6.5"
+version = "0.6.7"
 features = ["serde"]
 optional = true
 
 [dependencies.toml_datetime]
-version = "0.6.5"
+version = "0.6.8"
 
 [dependencies.winnow]
-version = "0.5.0"
+version = "0.6.18"
 optional = true
 
 [dev-dependencies.libtest-mimic]
-version = "0.7.0"
+version = "0.7.2"
+
+[dev-dependencies.proptest]
+version = "1.5.0"
 
 [dev-dependencies.serde_json]
-version = "1.0.96"
+version = "1.0.116"
 
 [dev-dependencies.snapbox]
-version = "0.4.11"
-features = ["harness"]
+version = "0.6.0"
 
 [dev-dependencies.toml-test-data]
-version = "1.8.0"
+version = "1.11.0"
 
 [dev-dependencies.toml-test-harness]
 version = "0.4.8"
@@ -178,3 +196,73 @@
     "dep:serde_spanned",
 ]
 unbounded = []
+
+[lints.clippy]
+bool_assert_comparison = "allow"
+branches_sharing_code = "allow"
+checked_conversions = "warn"
+collapsible_else_if = "allow"
+create_dir = "warn"
+dbg_macro = "warn"
+debug_assert_with_mut_call = "warn"
+doc_markdown = "warn"
+empty_enum = "warn"
+enum_glob_use = "warn"
+expl_impl_clone_on_copy = "warn"
+explicit_deref_methods = "warn"
+explicit_into_iter_loop = "warn"
+fallible_impl_from = "warn"
+filter_map_next = "warn"
+flat_map_option = "warn"
+float_cmp_const = "warn"
+fn_params_excessive_bools = "warn"
+from_iter_instead_of_collect = "warn"
+if_same_then_else = "allow"
+implicit_clone = "warn"
+imprecise_flops = "warn"
+inconsistent_struct_constructor = "warn"
+inefficient_to_string = "warn"
+infinite_loop = "warn"
+invalid_upcast_comparisons = "warn"
+large_digit_groups = "warn"
+large_stack_arrays = "warn"
+large_types_passed_by_value = "warn"
+let_and_return = "allow"
+linkedlist = "warn"
+lossy_float_literal = "warn"
+macro_use_imports = "warn"
+mem_forget = "warn"
+mutex_integer = "warn"
+needless_continue = "warn"
+needless_for_each = "warn"
+negative_feature_names = "warn"
+path_buf_push_overwrite = "warn"
+ptr_as_ptr = "warn"
+rc_mutex = "warn"
+redundant_feature_names = "warn"
+ref_option_ref = "warn"
+rest_pat_in_fully_bound_structs = "warn"
+same_functions_in_if_condition = "warn"
+self_named_module_files = "warn"
+semicolon_if_nothing_returned = "warn"
+str_to_string = "warn"
+string_add = "warn"
+string_add_assign = "warn"
+string_lit_as_bytes = "warn"
+string_to_string = "warn"
+todo = "warn"
+trait_duplication_in_bounds = "warn"
+verbose_file_reads = "warn"
+wildcard_imports = "warn"
+zero_sized_map_values = "warn"
+
+[lints.rust]
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "warn"
+unused_lifetimes = "warn"
+unused_macro_rules = "warn"
+unused_qualifications = "warn"
+
+[lints.rust.rust_2018_idioms]
+level = "warn"
+priority = -1
diff --git a/crates/toml_edit/METADATA b/crates/toml_edit/METADATA
index ab7f1ff..80c7320 100644
--- a/crates/toml_edit/METADATA
+++ b/crates/toml_edit/METADATA
@@ -1,17 +1,17 @@
 name: "toml_edit"
 description: "Yet another format-preserving TOML parser."
 third_party {
-  version: "0.22.4"
+  version: "0.22.20"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2024
-    month: 2
-    day: 7
+    year: 2025
+    month: 1
+    day: 14
   }
   homepage: "https://crates.io/crates/toml_edit"
   identifier {
     type: "Archive"
-    value: "https://static.crates.io/crates/toml_edit/toml_edit-0.22.4.crate"
-    version: "0.22.4"
+    value: "https://static.crates.io/crates/toml_edit/toml_edit-0.22.20.crate"
+    version: "0.22.20"
   }
 }
diff --git a/crates/toml_edit/examples/visit.rs b/crates/toml_edit/examples/visit.rs
index cd7f851..3f52334 100644
--- a/crates/toml_edit/examples/visit.rs
+++ b/crates/toml_edit/examples/visit.rs
@@ -1,9 +1,9 @@
 //! Example for how to use `VisitMut` to iterate over a table.
 
 use std::collections::BTreeSet;
-use toml_edit::visit::*;
-use toml_edit::visit_mut::*;
-use toml_edit::{Array, Document, InlineTable, Item, KeyMut, Table, Value};
+use toml_edit::visit::{visit_table_like_kv, Visit};
+use toml_edit::visit_mut::{visit_table_like_kv_mut, visit_table_mut, VisitMut};
+use toml_edit::{Array, DocumentMut, InlineTable, Item, KeyMut, Table, Value};
 
 /// This models the visit state for dependency keys in a `Cargo.toml`.
 ///
@@ -159,7 +159,7 @@
     }
 }
 
-/// This is the input provided to visit_mut_example.
+/// This is the input provided to `visit_mut_example`.
 static INPUT: &str = r#"
 [package]
 name = "my-package"
@@ -195,7 +195,7 @@
 version = "0.4"
 "#;
 
-/// This is the output produced by visit_mut_example.
+/// This is the output produced by `visit_mut_example`.
 #[cfg(test)]
 static VISIT_MUT_OUTPUT: &str = r#"
 [package]
@@ -223,7 +223,7 @@
 flate2 = { version = "0.4" }
 "#;
 
-fn visit_example(document: &Document) -> BTreeSet<&str> {
+fn visit_example(document: &DocumentMut) -> BTreeSet<&str> {
     let mut visitor = DependencyNameVisitor {
         state: VisitState::Root,
         names: BTreeSet::new(),
@@ -234,7 +234,7 @@
     visitor.names
 }
 
-fn visit_mut_example(document: &mut Document) {
+fn visit_mut_example(document: &mut DocumentMut) {
     let mut visitor = NormalizeDependencyTablesVisitor {
         state: VisitState::Root,
     };
@@ -243,7 +243,7 @@
 }
 
 fn main() {
-    let mut document: Document = INPUT.parse().expect("input is valid TOML");
+    let mut document: DocumentMut = INPUT.parse().expect("input is valid TOML");
 
     println!("** visit example");
     println!("{:?}", visit_example(&document));
@@ -256,7 +256,7 @@
 #[cfg(test)]
 #[test]
 fn visit_correct() {
-    let document: Document = INPUT.parse().expect("input is valid TOML");
+    let document: DocumentMut = INPUT.parse().expect("input is valid TOML");
 
     let names = visit_example(&document);
     let expected = vec![
@@ -277,7 +277,7 @@
 #[cfg(test)]
 #[test]
 fn visit_mut_correct() {
-    let mut document: Document = INPUT.parse().expect("input is valid TOML");
+    let mut document: DocumentMut = INPUT.parse().expect("input is valid TOML");
 
     visit_mut_example(&mut document);
     assert_eq!(format!("{}", document), VISIT_MUT_OUTPUT);
diff --git a/crates/toml_edit/src/array.rs b/crates/toml_edit/src/array.rs
index 377f676..3e69d5c 100644
--- a/crates/toml_edit/src/array.rs
+++ b/crates/toml_edit/src/array.rs
@@ -87,8 +87,10 @@
         &self.decor
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.span.clone()
     }
 
@@ -130,7 +132,7 @@
         self.values.len()
     }
 
-    /// Return true iff `self.len() == 0`.
+    /// Return true if `self.len() == 0`.
     ///
     /// # Examples
     ///
@@ -148,7 +150,7 @@
 
     /// Clears the array, removing all values. Keeps the allocated memory for reuse.
     pub fn clear(&mut self) {
-        self.values.clear()
+        self.values.clear();
     }
 
     /// Returns a reference to the value at the given index, or `None` if the index is out of
@@ -174,8 +176,8 @@
     /// ```
     pub fn push<V: Into<Value>>(&mut self, v: V) {
         self.value_op(v.into(), true, |items, value| {
-            items.push(Item::Value(value))
-        })
+            items.push(Item::Value(value));
+        });
     }
 
     /// Appends a new, already formatted value to the end of the array.
@@ -211,8 +213,8 @@
     /// ```
     pub fn insert<V: Into<Value>>(&mut self, index: usize, v: V) {
         self.value_op(v.into(), true, |items, value| {
-            items.insert(index, Item::Value(value))
-        })
+            items.insert(index, Item::Value(value));
+        });
     }
 
     /// Inserts an already formatted value at the given position within the array, shifting all
@@ -235,7 +237,7 @@
     /// # }
     /// ```
     pub fn insert_formatted(&mut self, index: usize, v: Value) {
-        self.values.insert(index, Item::Value(v))
+        self.values.insert(index, Item::Value(v));
     }
 
     /// Replaces the element at the given position within the array, preserving existing formatting.
@@ -350,7 +352,7 @@
                 (None, Some(_)) => std::cmp::Ordering::Less,
                 (Some(lhs), Some(rhs)) => compare(lhs, rhs),
             }
-        })
+        });
     }
 
     /// Sorts the array with a key extraction function.
diff --git a/crates/toml_edit/src/array_of_tables.rs b/crates/toml_edit/src/array_of_tables.rs
index 2e602a2..b562f89 100644
--- a/crates/toml_edit/src/array_of_tables.rs
+++ b/crates/toml_edit/src/array_of_tables.rs
@@ -32,8 +32,10 @@
         a
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.span.clone()
     }
 
@@ -62,14 +64,14 @@
         self.values.len()
     }
 
-    /// Returns true iff `self.len() == 0`.
+    /// Returns true if `self.len() == 0`.
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
 
     /// Removes all the tables.
     pub fn clear(&mut self) {
-        self.values.clear()
+        self.values.clear();
     }
 
     /// Returns an optional reference to the table.
diff --git a/crates/toml_edit/src/de/array.rs b/crates/toml_edit/src/de/array.rs
index adc5401..eeedd7b 100644
--- a/crates/toml_edit/src/de/array.rs
+++ b/crates/toml_edit/src/de/array.rs
@@ -48,7 +48,7 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for ArrayDeserializer {
+impl<'de> serde::de::IntoDeserializer<'de, Error> for ArrayDeserializer {
     type Deserializer = Self;
 
     fn into_deserializer(self) -> Self::Deserializer {
diff --git a/crates/toml_edit/src/de/key.rs b/crates/toml_edit/src/de/key.rs
index a3b2825..d7ce30e 100644
--- a/crates/toml_edit/src/de/key.rs
+++ b/crates/toml_edit/src/de/key.rs
@@ -13,7 +13,7 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, Error> for KeyDeserializer {
+impl<'de> IntoDeserializer<'de, Error> for KeyDeserializer {
     type Deserializer = Self;
 
     fn into_deserializer(self) -> Self::Deserializer {
@@ -81,7 +81,7 @@
 }
 
 impl<'de> serde::de::EnumAccess<'de> for KeyDeserializer {
-    type Error = super::Error;
+    type Error = Error;
     type Variant = UnitOnly<Self::Error>;
 
     fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error>
diff --git a/crates/toml_edit/src/de/mod.rs b/crates/toml_edit/src/de/mod.rs
index 9b8a2c7..391153f 100644
--- a/crates/toml_edit/src/de/mod.rs
+++ b/crates/toml_edit/src/de/mod.rs
@@ -16,7 +16,6 @@
 use datetime::DatetimeDeserializer;
 use key::KeyDeserializer;
 use spanned::SpannedDeserializer;
-use table::TableMapAccess;
 use table_enum::TableEnumDeserializer;
 
 pub use value::ValueDeserializer;
@@ -39,7 +38,7 @@
 
     /// Add key while unwinding
     pub fn add_key(&mut self, key: String) {
-        self.inner.add_key(key)
+        self.inner.add_key(key);
     }
 
     /// What went wrong
@@ -67,7 +66,7 @@
 }
 
 impl std::fmt::Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         self.inner.fmt(f)
     }
 }
@@ -86,17 +85,17 @@
 
 impl std::error::Error for Error {}
 
-/// Convert a value into `T`.
+/// Convert a TOML [documents][crate::DocumentMut] into `T`.
 #[cfg(feature = "parse")]
 pub fn from_str<T>(s: &'_ str) -> Result<T, Error>
 where
     T: DeserializeOwned,
 {
-    let de = s.parse::<Deserializer>()?;
+    let de = Deserializer::parse(s)?;
     T::deserialize(de)
 }
 
-/// Convert a value into `T`.
+/// Convert a TOML [documents][crate::DocumentMut] into `T`.
 #[cfg(feature = "parse")]
 pub fn from_slice<T>(s: &'_ [u8]) -> Result<T, Error>
 where
@@ -106,24 +105,51 @@
     from_str(s)
 }
 
-/// Convert a document into `T`.
-pub fn from_document<T>(d: crate::Document) -> Result<T, Error>
+/// Convert a [`DocumentMut`][crate::DocumentMut] into `T`.
+pub fn from_document<T>(d: impl Into<Deserializer>) -> Result<T, Error>
 where
     T: DeserializeOwned,
 {
-    let deserializer = Deserializer::new(d);
+    let deserializer = d.into();
     T::deserialize(deserializer)
 }
 
-/// Deserialization for TOML [documents][crate::Document].
-pub struct Deserializer {
-    input: crate::Document,
+/// Deserialization for TOML [documents][crate::DocumentMut].
+pub struct Deserializer<S = String> {
+    root: crate::Item,
+    raw: Option<S>,
 }
 
 impl Deserializer {
     /// Deserialization implementation for TOML.
-    pub fn new(input: crate::Document) -> Self {
-        Self { input }
+    #[deprecated(since = "0.22.6", note = "Replaced with `Deserializer::from`")]
+    pub fn new(input: crate::DocumentMut) -> Self {
+        Self::from(input)
+    }
+}
+
+#[cfg(feature = "parse")]
+impl<S: AsRef<str>> Deserializer<S> {
+    /// Parse a TOML document
+    pub fn parse(raw: S) -> Result<Self, Error> {
+        crate::ImDocument::parse(raw)
+            .map(Self::from)
+            .map_err(Into::into)
+    }
+}
+
+impl From<crate::DocumentMut> for Deserializer {
+    fn from(doc: crate::DocumentMut) -> Self {
+        let crate::DocumentMut { root, .. } = doc;
+        Self { root, raw: None }
+    }
+}
+
+impl<S> From<crate::ImDocument<S>> for Deserializer<S> {
+    fn from(doc: crate::ImDocument<S>) -> Self {
+        let crate::ImDocument { root, raw, .. } = doc;
+        let raw = Some(raw);
+        Self { root, raw }
     }
 }
 
@@ -133,27 +159,26 @@
 
     /// Parses a document from a &str
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let d = crate::parser::parse_document(s).map_err(Error::from)?;
-        Ok(Self::new(d))
+        let doc: crate::ImDocument<_> = s.parse().map_err(Error::from)?;
+        Ok(Deserializer::from(doc))
     }
 }
 
 // Note: this is wrapped by `toml::de::Deserializer` and any trait methods
 // implemented here need to be wrapped there
-impl<'de> serde::Deserializer<'de> for Deserializer {
+impl<'de, S: Into<String>> serde::Deserializer<'de> for Deserializer<S> {
     type Error = Error;
 
     fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
     where
         V: serde::de::Visitor<'de>,
     {
-        let original = self.input.original;
-        self.input
-            .root
+        let raw = self.raw;
+        self.root
             .into_deserializer()
             .deserialize_any(visitor)
             .map_err(|mut e: Self::Error| {
-                e.inner.set_original(original);
+                e.inner.set_raw(raw.map(|r| r.into()));
                 e
             })
     }
@@ -164,13 +189,12 @@
     where
         V: serde::de::Visitor<'de>,
     {
-        let original = self.input.original;
-        self.input
-            .root
+        let raw = self.raw;
+        self.root
             .into_deserializer()
             .deserialize_option(visitor)
             .map_err(|mut e: Self::Error| {
-                e.inner.set_original(original);
+                e.inner.set_raw(raw.map(|r| r.into()));
                 e
             })
     }
@@ -183,13 +207,12 @@
     where
         V: serde::de::Visitor<'de>,
     {
-        let original = self.input.original;
-        self.input
-            .root
+        let raw = self.raw;
+        self.root
             .into_deserializer()
             .deserialize_newtype_struct(name, visitor)
             .map_err(|mut e: Self::Error| {
-                e.inner.set_original(original);
+                e.inner.set_raw(raw.map(|r| r.into()));
                 e
             })
     }
@@ -203,13 +226,12 @@
     where
         V: serde::de::Visitor<'de>,
     {
-        let original = self.input.original;
-        self.input
-            .root
+        let raw = self.raw;
+        self.root
             .into_deserializer()
             .deserialize_struct(name, fields, visitor)
             .map_err(|mut e: Self::Error| {
-                e.inner.set_original(original);
+                e.inner.set_raw(raw.map(|r| r.into()));
                 e
             })
     }
@@ -224,13 +246,12 @@
     where
         V: serde::de::Visitor<'de>,
     {
-        let original = self.input.original;
-        self.input
-            .root
+        let raw = self.raw;
+        self.root
             .into_deserializer()
             .deserialize_enum(name, variants, visitor)
             .map_err(|mut e: Self::Error| {
-                e.inner.set_original(original);
+                e.inner.set_raw(raw.map(|r| r.into()));
                 e
             })
     }
@@ -242,7 +263,7 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for Deserializer {
+impl<'de> serde::de::IntoDeserializer<'de, Error> for Deserializer {
     type Deserializer = Deserializer;
 
     fn into_deserializer(self) -> Self::Deserializer {
@@ -250,11 +271,19 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for crate::Document {
+impl<'de> serde::de::IntoDeserializer<'de, Error> for crate::DocumentMut {
     type Deserializer = Deserializer;
 
     fn into_deserializer(self) -> Self::Deserializer {
-        Deserializer::new(self)
+        Deserializer::from(self)
+    }
+}
+
+impl<'de> serde::de::IntoDeserializer<'de, Error> for crate::ImDocument<String> {
+    type Deserializer = Deserializer;
+
+    fn into_deserializer(self) -> Self::Deserializer {
+        Deserializer::from(self)
     }
 }
 
diff --git a/crates/toml_edit/src/de/table.rs b/crates/toml_edit/src/de/table.rs
index 33aa397..85adcc0 100644
--- a/crates/toml_edit/src/de/table.rs
+++ b/crates/toml_edit/src/de/table.rs
@@ -16,7 +16,7 @@
     where
         V: serde::de::Visitor<'de>,
     {
-        visitor.visit_map(crate::de::TableMapAccess::new(self))
+        visitor.visit_map(TableMapAccess::new(self))
     }
 
     // `None` is interpreted as a missing field so be sure to implement `Some`
@@ -68,17 +68,17 @@
         V: serde::de::Visitor<'de>,
     {
         if self.items.is_empty() {
-            Err(crate::de::Error::custom(
+            Err(Error::custom(
                 "wanted exactly 1 element, found 0 elements",
                 self.span,
             ))
         } else if self.items.len() != 1 {
-            Err(crate::de::Error::custom(
+            Err(Error::custom(
                 "wanted exactly 1 element, more than 1 element",
                 self.span,
             ))
         } else {
-            visitor.visit_enum(crate::de::TableMapAccess::new(self))
+            visitor.visit_enum(TableMapAccess::new(self))
         }
     }
 
@@ -89,7 +89,7 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for TableDeserializer {
+impl<'de> IntoDeserializer<'de, Error> for TableDeserializer {
     type Deserializer = TableDeserializer;
 
     fn into_deserializer(self) -> Self::Deserializer {
diff --git a/crates/toml_edit/src/de/value.rs b/crates/toml_edit/src/de/value.rs
index ba6ce6d..d7fb4a4 100644
--- a/crates/toml_edit/src/de/value.rs
+++ b/crates/toml_edit/src/de/value.rs
@@ -11,6 +11,8 @@
 /// # Example
 ///
 /// ```
+/// # #[cfg(feature = "parse")] {
+/// # #[cfg(feature = "display")] {
 /// use serde::Deserialize;
 ///
 /// #[derive(Deserialize)]
@@ -29,6 +31,8 @@
 /// let config = Config::deserialize(deserializer).unwrap();
 /// assert_eq!(config.title, "TOML Example");
 /// assert_eq!(config.owner.name, "Lisa");
+/// # }
+/// # }
 /// ```
 pub struct ValueDeserializer {
     input: crate::Item,
@@ -162,7 +166,7 @@
                     e.set_span(span);
                 }
                 e
-            })?
+            })?;
         }
 
         self.deserialize_any(visitor)
@@ -185,12 +189,12 @@
             }
             crate::Item::Value(crate::Value::InlineTable(v)) => {
                 if v.is_empty() {
-                    Err(crate::de::Error::custom(
+                    Err(Error::custom(
                         "wanted exactly 1 element, found 0 elements",
                         v.span(),
                     ))
                 } else if v.len() != 1 {
-                    Err(crate::de::Error::custom(
+                    Err(Error::custom(
                         "wanted exactly 1 element, more than 1 element",
                         v.span(),
                     ))
@@ -202,7 +206,7 @@
             crate::Item::Table(v) => v
                 .into_deserializer()
                 .deserialize_enum(name, variants, visitor),
-            e => Err(crate::de::Error::custom("wanted string or table", e.span())),
+            e => Err(Error::custom("wanted string or table", e.span())),
         }
         .map_err(|mut e: Self::Error| {
             if e.span().is_none() {
@@ -219,7 +223,7 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for ValueDeserializer {
+impl<'de> serde::de::IntoDeserializer<'de, Error> for ValueDeserializer {
     type Deserializer = Self;
 
     fn into_deserializer(self) -> Self::Deserializer {
@@ -227,7 +231,7 @@
     }
 }
 
-impl<'de> serde::de::IntoDeserializer<'de, crate::de::Error> for crate::Value {
+impl<'de> serde::de::IntoDeserializer<'de, Error> for crate::Value {
     type Deserializer = ValueDeserializer;
 
     fn into_deserializer(self) -> Self::Deserializer {
diff --git a/crates/toml_edit/src/document.rs b/crates/toml_edit/src/document.rs
index f20e61a..40edf76 100644
--- a/crates/toml_edit/src/document.rs
+++ b/crates/toml_edit/src/document.rs
@@ -3,17 +3,117 @@
 use crate::table::Iter;
 use crate::{Item, RawString, Table};
 
-/// Type representing a TOML document
+/// Type representing a parsed TOML document
 #[derive(Debug, Clone)]
-pub struct Document {
+pub struct ImDocument<S> {
     pub(crate) root: Item,
     // Trailing comments and whitespaces
     pub(crate) trailing: RawString,
-    pub(crate) original: Option<String>,
-    pub(crate) span: Option<std::ops::Range<usize>>,
+    pub(crate) raw: S,
 }
 
-impl Document {
+impl ImDocument<&'static str> {
+    /// Creates an empty document
+    pub fn new() -> Self {
+        Default::default()
+    }
+}
+
+#[cfg(feature = "parse")]
+impl<S: AsRef<str>> ImDocument<S> {
+    /// Parse a TOML document
+    pub fn parse(raw: S) -> Result<Self, crate::TomlError> {
+        crate::parser::parse_document(raw)
+    }
+}
+
+impl<S: AsRef<str>> ImDocument<S> {
+    /// # Panics
+    ///
+    /// If run on on a [`DocumentMut`] not generated by the parser
+    pub(crate) fn despan(&mut self) {
+        self.root.despan(self.raw.as_ref());
+        self.trailing.despan(self.raw.as_ref());
+    }
+}
+
+impl<S> ImDocument<S> {
+    /// Returns a reference to the root item.
+    pub fn as_item(&self) -> &Item {
+        &self.root
+    }
+
+    /// Returns a reference to the root table.
+    pub fn as_table(&self) -> &Table {
+        self.root.as_table().expect("root should always be a table")
+    }
+
+    /// Returns an iterator over the root table.
+    pub fn iter(&self) -> Iter<'_> {
+        self.as_table().iter()
+    }
+
+    /// Whitespace after last element
+    pub fn trailing(&self) -> &RawString {
+        &self.trailing
+    }
+}
+
+impl<S: AsRef<str>> ImDocument<S> {
+    /// Access the raw, unparsed document
+    pub fn raw(&self) -> &str {
+        self.raw.as_ref()
+    }
+}
+
+impl<S: AsRef<str>> ImDocument<S> {
+    /// Allow editing of the [`DocumentMut`]
+    pub fn into_mut(mut self) -> DocumentMut {
+        self.despan();
+        DocumentMut {
+            root: self.root,
+            trailing: self.trailing,
+        }
+    }
+}
+
+impl Default for ImDocument<&'static str> {
+    fn default() -> Self {
+        Self {
+            root: Item::Table(Table::with_pos(Some(0))),
+            trailing: Default::default(),
+            raw: "",
+        }
+    }
+}
+
+#[cfg(feature = "parse")]
+impl FromStr for ImDocument<String> {
+    type Err = crate::TomlError;
+
+    /// Parses a document from a &str
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Self::parse(s.to_owned())
+    }
+}
+
+impl<S> std::ops::Deref for ImDocument<S> {
+    type Target = Table;
+
+    fn deref(&self) -> &Self::Target {
+        self.as_table()
+    }
+}
+
+/// Type representing a TOML document
+#[derive(Debug, Clone)]
+pub struct DocumentMut {
+    pub(crate) root: Item,
+    // Trailing comments and whitespaces
+    pub(crate) trailing: RawString,
+}
+
+impl DocumentMut {
     /// Creates an empty document
     pub fn new() -> Self {
         Default::default()
@@ -55,41 +155,29 @@
     pub fn trailing(&self) -> &RawString {
         &self.trailing
     }
-
-    /// # Panics
-    ///
-    /// If run on on a `Document` not generated by the parser
-    pub(crate) fn despan(&mut self) {
-        self.span = None;
-        self.root.despan(self.original.as_deref().unwrap());
-        self.trailing.despan(self.original.as_deref().unwrap());
-    }
 }
 
-impl Default for Document {
+impl Default for DocumentMut {
     fn default() -> Self {
         Self {
             root: Item::Table(Table::with_pos(Some(0))),
             trailing: Default::default(),
-            original: Default::default(),
-            span: Default::default(),
         }
     }
 }
 
 #[cfg(feature = "parse")]
-impl FromStr for Document {
+impl FromStr for DocumentMut {
     type Err = crate::TomlError;
 
     /// Parses a document from a &str
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let mut d = crate::parser::parse_document(s)?;
-        d.despan();
-        Ok(d)
+        let im = ImDocument::from_str(s)?;
+        Ok(im.into_mut())
     }
 }
 
-impl std::ops::Deref for Document {
+impl std::ops::Deref for DocumentMut {
     type Target = Table;
 
     fn deref(&self) -> &Self::Target {
@@ -97,13 +185,13 @@
     }
 }
 
-impl std::ops::DerefMut for Document {
+impl std::ops::DerefMut for DocumentMut {
     fn deref_mut(&mut self) -> &mut Self::Target {
         self.as_table_mut()
     }
 }
 
-impl From<Table> for Document {
+impl From<Table> for DocumentMut {
     fn from(root: Table) -> Self {
         Self {
             root: Item::Table(root),
@@ -111,3 +199,13 @@
         }
     }
 }
+
+#[test]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
+fn default_roundtrip() {
+    DocumentMut::default()
+        .to_string()
+        .parse::<DocumentMut>()
+        .unwrap();
+}
diff --git a/crates/toml_edit/src/encode.rs b/crates/toml_edit/src/encode.rs
index 30b153a..b45f0e3 100644
--- a/crates/toml_edit/src/encode.rs
+++ b/crates/toml_edit/src/encode.rs
@@ -1,16 +1,18 @@
 use std::borrow::Cow;
 use std::fmt::{Display, Formatter, Result, Write};
 
-use toml_datetime::*;
+use toml_datetime::Datetime;
 
-use crate::document::Document;
 use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
 use crate::key::Key;
 use crate::repr::{Formatted, Repr, ValueRepr};
-use crate::table::{DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_TABLE_DECOR};
+use crate::table::{
+    DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_ROOT_DECOR, DEFAULT_TABLE_DECOR,
+};
 use crate::value::{
     DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
 };
+use crate::DocumentMut;
 use crate::{Array, InlineTable, Item, Table, Value};
 
 pub(crate) fn encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result {
@@ -195,8 +197,11 @@
     }
 }
 
-impl Display for Document {
+impl Display for DocumentMut {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+        let decor = self.decor();
+        decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
+
         let mut path = Vec::new();
         let mut last_position = 0;
         let mut tables = Vec::new();
@@ -212,17 +217,10 @@
         tables.sort_by_key(|&(id, _, _, _)| id);
         let mut first_table = true;
         for (_, table, path, is_array) in tables {
-            visit_table(
-                f,
-                self.original.as_deref(),
-                table,
-                &path,
-                is_array,
-                &mut first_table,
-            )?;
+            visit_table(f, None, table, &path, is_array, &mut first_table)?;
         }
-        self.trailing()
-            .encode_with_default(f, self.original.as_deref(), "")
+        decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
+        self.trailing().encode_with_default(f, None, "")
     }
 }
 
@@ -334,12 +332,7 @@
     style: Option<StringStyle>,
     literal: Option<bool>,
 ) -> Repr {
-    let (style, literal) = match (style, literal) {
-        (Some(style), Some(literal)) => (style, literal),
-        (_, Some(literal)) => (infer_style(value).0, literal),
-        (Some(style), _) => (style, infer_style(value).1),
-        (_, _) => infer_style(value),
-    };
+    let (style, literal) = infer_style(value, style, literal);
 
     let mut output = String::with_capacity(value.len() * 2);
     if literal {
@@ -355,7 +348,7 @@
                 '\u{a}' => match style {
                     StringStyle::NewlineTriple => output.push('\n'),
                     StringStyle::OnelineSingle => output.push_str("\\n"),
-                    _ => unreachable!(),
+                    StringStyle::OnelineTriple => unreachable!(),
                 },
                 '\u{c}' => output.push_str("\\f"),
                 '\u{d}' => output.push_str("\\r"),
@@ -417,7 +410,38 @@
     }
 }
 
-fn infer_style(value: &str) -> (StringStyle, bool) {
+fn infer_style(
+    value: &str,
+    style: Option<StringStyle>,
+    literal: Option<bool>,
+) -> (StringStyle, bool) {
+    match (style, literal) {
+        (Some(style), Some(literal)) => (style, literal),
+        (None, Some(literal)) => (infer_all_style(value).0, literal),
+        (Some(style), None) => {
+            let literal = infer_literal(value);
+            (style, literal)
+        }
+        (None, None) => infer_all_style(value),
+    }
+}
+
+fn infer_literal(value: &str) -> bool {
+    #[cfg(feature = "parse")]
+    {
+        use winnow::stream::ContainsToken as _;
+        (value.contains('"') | value.contains('\\'))
+            && value
+                .chars()
+                .all(|c| crate::parser::strings::LITERAL_CHAR.contains_token(c))
+    }
+    #[cfg(not(feature = "parse"))]
+    {
+        false
+    }
+}
+
+fn infer_all_style(value: &str) -> (StringStyle, bool) {
     // We need to determine:
     // - if we are a "multi-line" pretty (if there are \n)
     // - if ['''] appears if multi or ['] if single
@@ -443,10 +467,13 @@
                 if found_singles > max_found_singles {
                     max_found_singles = found_singles;
                 }
-                found_singles = 0
+                found_singles = 0;
             }
             match ch {
                 '\t' => {}
+                '"' => {
+                    prefer_literal = true;
+                }
                 '\\' => {
                     prefer_literal = true;
                 }
@@ -526,3 +553,45 @@
         Repr::new_unchecked(self.to_string())
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        #[cfg(feature = "parse")]
+        fn parseable_string(string in "\\PC*") {
+            let string = Value::from(string);
+            let encoded = string.to_string();
+            let _: Value = encoded.parse().unwrap_or_else(|err| {
+                panic!("error: {err}
+
+string:
+```
+{string}
+```
+")
+            });
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(feature = "parse")]
+        fn parseable_key(string in "\\PC*") {
+            let string = Key::new(string);
+            let encoded = string.to_string();
+            let _: Key = encoded.parse().unwrap_or_else(|err| {
+                panic!("error: {err}
+
+string:
+```
+{string}
+```
+")
+            });
+        }
+    }
+}
diff --git a/crates/toml_edit/src/error.rs b/crates/toml_edit/src/error.rs
index a983019..57c21ef 100644
--- a/crates/toml_edit/src/error.rs
+++ b/crates/toml_edit/src/error.rs
@@ -5,7 +5,7 @@
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct TomlError {
     message: String,
-    original: Option<String>,
+    raw: Option<String>,
     keys: Vec<String>,
     span: Option<std::ops::Range<usize>>,
 }
@@ -17,35 +17,42 @@
             crate::parser::prelude::Input<'_>,
             winnow::error::ContextError,
         >,
-        mut original: crate::parser::prelude::Input<'_>,
+        mut raw: crate::parser::prelude::Input<'_>,
     ) -> Self {
         use winnow::stream::Stream;
 
-        let offset = error.offset();
-        let span = if offset == original.len() {
-            offset..offset
-        } else {
-            offset..(offset + 1)
-        };
-
         let message = error.inner().to_string();
-        let original = original.finish();
+        let raw = raw.finish();
+        let raw = String::from_utf8(raw.to_owned()).expect("original document was utf8");
+
+        let offset = error.offset();
+        let offset = (0..=offset)
+            .rev()
+            .find(|index| raw.is_char_boundary(*index))
+            .unwrap_or(0);
+
+        let mut indices = raw[offset..].char_indices();
+        indices.next();
+        let len = if let Some((index, _)) = indices.next() {
+            index
+        } else {
+            raw.len() - offset
+        };
+        let span = offset..(offset + len);
 
         Self {
             message,
-            original: Some(
-                String::from_utf8(original.to_owned()).expect("original document was utf8"),
-            ),
+            raw: Some(raw),
             keys: Vec::new(),
             span: Some(span),
         }
     }
 
-    #[cfg(feature = "serde")]
+    #[cfg(any(feature = "serde", feature = "parse"))]
     pub(crate) fn custom(message: String, span: Option<std::ops::Range<usize>>) -> Self {
         Self {
             message,
-            original: None,
+            raw: None,
             keys: Vec::new(),
             span,
         }
@@ -72,8 +79,8 @@
     }
 
     #[cfg(feature = "serde")]
-    pub(crate) fn set_original(&mut self, original: Option<String>) {
-        self.original = original;
+    pub(crate) fn set_raw(&mut self, raw: Option<String>) {
+        self.raw = raw;
     }
 }
 
@@ -92,14 +99,17 @@
 impl Display for TomlError {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
         let mut context = false;
-        if let (Some(original), Some(span)) = (&self.original, self.span()) {
+        if let (Some(raw), Some(span)) = (&self.raw, self.span()) {
             context = true;
 
-            let (line, column) = translate_position(original.as_bytes(), span.start);
+            let (line, column) = translate_position(raw.as_bytes(), span.start);
             let line_num = line + 1;
             let col_num = column + 1;
             let gutter = line_num.to_string().len();
-            let content = original.split('\n').nth(line).expect("valid line number");
+            let content = raw.split('\n').nth(line).expect("valid line number");
+            let highlight_len = span.end - span.start;
+            // Allow highlight to go one past the line
+            let highlight_len = highlight_len.min(content.len().saturating_sub(column));
 
             writeln!(
                 f,
@@ -127,7 +137,7 @@
             // The span will be empty at eof, so we need to make sure we always print at least
             // one `^`
             write!(f, "^")?;
-            for _ in (span.start + 1)..(span.end.min(span.start + content.len())) {
+            for _ in 1..highlight_len {
                 write!(f, "^")?;
             }
             writeln!(f)?;
diff --git a/crates/toml_edit/src/index.rs b/crates/toml_edit/src/index.rs
index 276db79..cdf646f 100644
--- a/crates/toml_edit/src/index.rs
+++ b/crates/toml_edit/src/index.rs
@@ -1,8 +1,8 @@
 use std::ops;
 
-use crate::document::Document;
 use crate::key::Key;
 use crate::table::TableKeyValue;
+use crate::DocumentMut;
 use crate::{value, InlineTable, InternalString, Item, Table, Value};
 
 // copied from
@@ -141,7 +141,7 @@
     }
 }
 
-impl<'s> ops::Index<&'s str> for Document {
+impl<'s> ops::Index<&'s str> for DocumentMut {
     type Output = Item;
 
     fn index(&self, key: &'s str) -> &Item {
@@ -149,7 +149,7 @@
     }
 }
 
-impl<'s> ops::IndexMut<&'s str> for Document {
+impl<'s> ops::IndexMut<&'s str> for DocumentMut {
     fn index_mut(&mut self, key: &'s str) -> &mut Item {
         self.root.index_mut(key)
     }
diff --git a/crates/toml_edit/src/inline_table.rs b/crates/toml_edit/src/inline_table.rs
index 316637d..c0a58fe 100644
--- a/crates/toml_edit/src/inline_table.rs
+++ b/crates/toml_edit/src/inline_table.rs
@@ -148,8 +148,8 @@
     /// ```
     /// # #[cfg(feature = "parse")] {
     /// # #[cfg(feature = "display")] {
-    /// use toml_edit::Document;
-    /// let mut doc = "[a]\n[a.b]\n".parse::<Document>().expect("invalid toml");
+    /// use toml_edit::DocumentMut;
+    /// let mut doc = "[a]\n[a.b]\n".parse::<DocumentMut>().expect("invalid toml");
     ///
     /// doc["a"].as_table_mut().unwrap().set_implicit(true);
     /// assert_eq!(doc.to_string(), "[a.b]\n");
@@ -219,8 +219,10 @@
         &self.preamble
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.span.clone()
     }
 
@@ -261,14 +263,14 @@
         self.iter().count()
     }
 
-    /// Returns true iff the table is empty.
+    /// Returns true if the table is empty.
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
 
     /// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
     pub fn clear(&mut self) {
-        self.items.clear()
+        self.items.clear();
     }
 
     /// Gets the given key's corresponding entry in the Table for in-place manipulation.
@@ -352,7 +354,7 @@
         })
     }
 
-    /// Returns true iff the table contains given key.
+    /// Returns true if the table contains given key.
     pub fn contains_key(&self, key: &str) -> bool {
         if let Some(kv) = self.items.get(key) {
             kv.value.is_value()
@@ -566,13 +568,13 @@
         self.get_values()
     }
     fn fmt(&mut self) {
-        self.fmt()
+        self.fmt();
     }
     fn sort_values(&mut self) {
-        self.sort_values()
+        self.sort_values();
     }
     fn set_dotted(&mut self, yes: bool) {
-        self.set_dotted(yes)
+        self.set_dotted(yes);
     }
     fn is_dotted(&self) -> bool {
         self.is_dotted()
@@ -679,7 +681,7 @@
         self.entry.get_mut().value.as_value_mut().unwrap()
     }
 
-    /// Converts the OccupiedEntry into a mutable reference to the value in the entry
+    /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
     /// with a lifetime bound to the map itself
     pub fn into_mut(self) -> &'a mut Value {
         self.entry.into_mut().value.as_value_mut().unwrap()
@@ -720,7 +722,7 @@
         self.entry.key().as_str()
     }
 
-    /// Sets the value of the entry with the VacantEntry's key,
+    /// Sets the value of the entry with the `VacantEntry`'s key,
     /// and returns a mutable reference to it
     pub fn insert(self, value: Value) -> &'a mut Value {
         let entry = self.entry;
diff --git a/crates/toml_edit/src/internal_string.rs b/crates/toml_edit/src/internal_string.rs
index d4347d2..ca4c185 100644
--- a/crates/toml_edit/src/internal_string.rs
+++ b/crates/toml_edit/src/internal_string.rs
@@ -5,9 +5,9 @@
 #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct InternalString(Inner);
 
-#[cfg(feature = "kstring")]
+#[cfg(feature = "perf")]
 type Inner = kstring::KString;
-#[cfg(not(feature = "kstring"))]
+#[cfg(not(feature = "perf"))]
 type Inner = String;
 
 impl InternalString {
@@ -56,9 +56,9 @@
 impl From<&str> for InternalString {
     #[inline]
     fn from(s: &str) -> Self {
-        #[cfg(feature = "kstring")]
+        #[cfg(feature = "perf")]
         let inner = kstring::KString::from_ref(s);
-        #[cfg(not(feature = "kstring"))]
+        #[cfg(not(feature = "perf"))]
         let inner = String::from(s);
 
         InternalString(inner)
diff --git a/crates/toml_edit/src/item.rs b/crates/toml_edit/src/item.rs
index b58806e..601c235 100644
--- a/crates/toml_edit/src/item.rs
+++ b/crates/toml_edit/src/item.rs
@@ -1,6 +1,6 @@
 use std::str::FromStr;
 
-use toml_datetime::*;
+use toml_datetime::Datetime;
 
 use crate::array_of_tables::ArrayOfTables;
 use crate::table::TableLike;
@@ -21,11 +21,11 @@
 }
 
 impl Item {
-    /// Sets `self` to the given item iff `self` is none and
+    /// Sets `self` to the given item if `self` is none and
     /// returns a mutable reference to `self`.
     pub fn or_insert(&mut self, item: Item) -> &mut Item {
         if self.is_none() {
-            *self = item
+            *self = item;
         }
         self
     }
@@ -167,29 +167,29 @@
     // Starting private because the name is unclear
     pub(crate) fn make_item(&mut self) {
         let other = std::mem::take(self);
-        let other = match other.into_table().map(crate::Item::Table) {
+        let other = match other.into_table().map(Item::Table) {
             Ok(i) => i,
             Err(i) => i,
         };
-        let other = match other.into_array_of_tables().map(crate::Item::ArrayOfTables) {
+        let other = match other.into_array_of_tables().map(Item::ArrayOfTables) {
             Ok(i) => i,
             Err(i) => i,
         };
         *self = other;
     }
-    /// Returns true iff `self` is a value.
+    /// Returns true if `self` is a value.
     pub fn is_value(&self) -> bool {
         self.as_value().is_some()
     }
-    /// Returns true iff `self` is a table.
+    /// Returns true if `self` is a table.
     pub fn is_table(&self) -> bool {
         self.as_table().is_some()
     }
-    /// Returns true iff `self` is an array of tables.
+    /// Returns true if `self` is an array of tables.
     pub fn is_array_of_tables(&self) -> bool {
         self.as_array_of_tables().is_some()
     }
-    /// Returns true iff `self` is `None`.
+    /// Returns true if `self` is `None`.
     pub fn is_none(&self) -> bool {
         matches!(*self, Item::None)
     }
@@ -201,7 +201,7 @@
         self.as_value().and_then(Value::as_integer)
     }
 
-    /// Returns true iff `self` is an integer.
+    /// Returns true if `self` is an integer.
     pub fn is_integer(&self) -> bool {
         self.as_integer().is_some()
     }
@@ -211,7 +211,7 @@
         self.as_value().and_then(Value::as_float)
     }
 
-    /// Returns true iff `self` is a float.
+    /// Returns true if `self` is a float.
     pub fn is_float(&self) -> bool {
         self.as_float().is_some()
     }
@@ -221,7 +221,7 @@
         self.as_value().and_then(Value::as_bool)
     }
 
-    /// Returns true iff `self` is a boolean.
+    /// Returns true if `self` is a boolean.
     pub fn is_bool(&self) -> bool {
         self.as_bool().is_some()
     }
@@ -231,7 +231,7 @@
         self.as_value().and_then(Value::as_str)
     }
 
-    /// Returns true iff `self` is a string.
+    /// Returns true if `self` is a string.
     pub fn is_str(&self) -> bool {
         self.as_str().is_some()
     }
@@ -241,7 +241,7 @@
         self.as_value().and_then(Value::as_datetime)
     }
 
-    /// Returns true iff `self` is a date-time.
+    /// Returns true if `self` is a date-time.
     pub fn is_datetime(&self) -> bool {
         self.as_datetime().is_some()
     }
@@ -256,7 +256,7 @@
         self.as_value_mut().and_then(Value::as_array_mut)
     }
 
-    /// Returns true iff `self` is an array.
+    /// Returns true if `self` is an array.
     pub fn is_array(&self) -> bool {
         self.as_array().is_some()
     }
@@ -271,7 +271,7 @@
         self.as_value_mut().and_then(Value::as_inline_table_mut)
     }
 
-    /// Returns true iff `self` is an inline table.
+    /// Returns true if `self` is an inline table.
     pub fn is_inline_table(&self) -> bool {
         self.as_inline_table().is_some()
     }
@@ -292,13 +292,15 @@
         }
     }
 
-    /// Returns true iff `self` is either a table, or an inline table.
+    /// Returns true if `self` is either a table, or an inline table.
     pub fn is_table_like(&self) -> bool {
         self.as_table_like().is_some()
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         match self {
             Item::None => None,
             Item::Value(v) => v.span(),
@@ -361,7 +363,7 @@
 /// # Examples
 /// ```rust
 /// # #[cfg(feature = "display")] {
-/// # use snapbox::assert_eq;
+/// # #[cfg(feature = "parse")] {
 /// # use toml_edit::*;
 /// let mut table = Table::default();
 /// let mut array = Array::default();
@@ -370,12 +372,13 @@
 /// table["key1"] = value("value1");
 /// table["key2"] = value(42);
 /// table["key3"] = value(array);
-/// assert_eq(table.to_string(),
+/// assert_eq!(table.to_string(),
 /// r#"key1 = "value1"
 /// key2 = 42
 /// key3 = ["hello", '\, world']
 /// "#);
 /// # }
+/// # }
 /// ```
 pub fn value<V: Into<Value>>(v: V) -> Item {
     Item::Value(v.into())
@@ -390,3 +393,10 @@
 pub fn array() -> Item {
     Item::ArrayOfTables(ArrayOfTables::new())
 }
+
+#[test]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
+fn string_roundtrip() {
+    value("hello").to_string().parse::<Item>().unwrap();
+}
diff --git a/crates/toml_edit/src/key.rs b/crates/toml_edit/src/key.rs
index 2f4c30a..15fdd4d 100644
--- a/crates/toml_edit/src/key.rs
+++ b/crates/toml_edit/src/key.rs
@@ -113,7 +113,10 @@
     }
 
     /// Returns the surrounding whitespace
-    #[deprecated(since = "0.21.1", note = "Replaced with `decor_mut`")]
+    #[deprecated(
+        since = "0.21.1",
+        note = "Replaced with `dotted_decor_mut`, `leaf_decor_mut"
+    )]
     pub fn decor_mut(&mut self) -> &mut Decor {
         self.leaf_decor_mut()
     }
@@ -129,7 +132,7 @@
     }
 
     /// Returns the surrounding whitespace
-    #[deprecated(since = "0.21.1", note = "Replaced with `decor`")]
+    #[deprecated(since = "0.21.1", note = "Replaced with `dotted_decor`, `leaf_decor")]
     pub fn decor(&self) -> &Decor {
         self.leaf_decor()
     }
@@ -144,9 +147,10 @@
         &self.dotted_decor
     }
 
-    /// Returns the location within the original document
-    #[cfg(feature = "serde")]
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.repr.as_ref().and_then(|r| r.span())
     }
 
@@ -154,7 +158,7 @@
         self.leaf_decor.despan(input);
         self.dotted_decor.despan(input);
         if let Some(repr) = &mut self.repr {
-            repr.despan(input)
+            repr.despan(input);
         }
     }
 
@@ -285,17 +289,13 @@
             crate::encode::to_string_repr(
                 key,
                 Some(crate::encode::StringStyle::OnelineSingle),
-                Some(false),
+                None,
             )
         }
     }
     #[cfg(not(feature = "parse"))]
     {
-        crate::encode::to_string_repr(
-            key,
-            Some(crate::encode::StringStyle::OnelineSingle),
-            Some(false),
-        )
+        crate::encode::to_string_repr(key, Some(crate::encode::StringStyle::OnelineSingle), None)
     }
 }
 
@@ -355,12 +355,15 @@
 
     /// Returns a raw representation.
     #[cfg(feature = "display")]
-    pub fn display_repr(&self) -> Cow<str> {
+    pub fn display_repr(&self) -> Cow<'_, str> {
         self.key.display_repr()
     }
 
     /// Returns the surrounding whitespace
-    #[deprecated(since = "0.21.1", note = "Replaced with `decor_mut`")]
+    #[deprecated(
+        since = "0.21.1",
+        note = "Replaced with `dotted_decor_mut`, `leaf_decor_mut"
+    )]
     pub fn decor_mut(&mut self) -> &mut Decor {
         #![allow(deprecated)]
         self.key.decor_mut()
@@ -377,7 +380,7 @@
     }
 
     /// Returns the surrounding whitespace
-    #[deprecated(since = "0.21.1", note = "Replaced with `decor`")]
+    #[deprecated(since = "0.21.1", note = "Replaced with `dotted_decor`, `leaf_decor")]
     pub fn decor(&self) -> &Decor {
         #![allow(deprecated)]
         self.key.decor()
@@ -395,7 +398,7 @@
 
     /// Auto formats the key.
     pub fn fmt(&mut self) {
-        self.key.fmt()
+        self.key.fmt();
     }
 }
 
@@ -434,3 +437,10 @@
         std::fmt::Display::fmt(&self.key, f)
     }
 }
+
+#[test]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
+fn string_roundtrip() {
+    Key::new("hello").to_string().parse::<Key>().unwrap();
+}
diff --git a/crates/toml_edit/src/lib.rs b/crates/toml_edit/src/lib.rs
index 25e3d20..c47b902 100644
--- a/crates/toml_edit/src/lib.rs
+++ b/crates/toml_edit/src/lib.rs
@@ -1,8 +1,3 @@
-#![deny(missing_docs)]
-// https://github.com/Marwes/combine/issues/172
-#![recursion_limit = "256"]
-#![cfg_attr(docsrs, feature(doc_auto_cfg))]
-
 //! # `toml_edit`
 //!
 //! This crate allows you to parse and modify toml
@@ -16,13 +11,13 @@
 //! ```rust
 //! # #[cfg(feature = "parse")] {
 //! # #[cfg(feature = "display")] {
-//! use toml_edit::{Document, value};
+//! use toml_edit::{DocumentMut, value};
 //!
 //! let toml = r#"
 //! "hello" = 'toml!' # comment
 //! ['a'.b]
 //! "#;
-//! let mut doc = toml.parse::<Document>().expect("invalid doc");
+//! let mut doc = toml.parse::<DocumentMut>().expect("invalid doc");
 //! assert_eq!(doc.to_string(), toml);
 //! // let's add a new key/value pair inside a.b: c = {d = "hello"}
 //! doc["a"]["b"]["c"]["d"] = value("hello");
@@ -43,23 +38,27 @@
 //! By default, values are created with default formatting
 //! ```rust
 //! # #[cfg(feature = "display")] {
-//! let mut doc = toml_edit::Document::new();
+//! # #[cfg(feature = "parse")] {
+//! let mut doc = toml_edit::DocumentMut::new();
 //! doc["foo"] = toml_edit::value("bar");
 //! let expected = r#"foo = "bar"
 //! "#;
 //! assert_eq!(doc.to_string(), expected);
 //! # }
+//! # }
 //! ```
 //!
 //! You can choose a custom TOML representation by parsing the value.
 //! ```rust
 //! # #[cfg(feature = "display")] {
-//! let mut doc = toml_edit::Document::new();
+//! # #[cfg(feature = "parse")] {
+//! let mut doc = toml_edit::DocumentMut::new();
 //! doc["foo"] = "'bar'".parse::<toml_edit::Item>().unwrap();
 //! let expected = r#"foo = 'bar'
 //! "#;
 //! assert_eq!(doc.to_string(), expected);
 //! # }
+//! # }
 //! ```
 //!
 //! ## Limitations
@@ -70,6 +69,13 @@
 //!
 //! [`toml`]: https://docs.rs/toml/latest/toml/
 
+// https://github.com/Marwes/combine/issues/172
+#![recursion_limit = "256"]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+#![warn(missing_docs)]
+#![warn(clippy::print_stderr)]
+#![warn(clippy::print_stdout)]
+
 mod array;
 mod array_of_tables;
 mod document;
@@ -100,7 +106,11 @@
 pub use crate::array_of_tables::{
     ArrayOfTables, ArrayOfTablesIntoIter, ArrayOfTablesIter, ArrayOfTablesIterMut,
 };
-pub use crate::document::Document;
+/// Deprecated, replaced with [`DocumentMut`]
+#[deprecated(since = "0.22.6", note = "Replaced with `DocumentMut`")]
+pub type Document = DocumentMut;
+pub use crate::document::DocumentMut;
+pub use crate::document::ImDocument;
 pub use crate::error::TomlError;
 pub use crate::inline_table::{
     InlineEntry, InlineOccupiedEntry, InlineTable, InlineTableIntoIter, InlineTableIter,
diff --git a/crates/toml_edit/src/parser/array.rs b/crates/toml_edit/src/parser/array.rs
index 0783191..d246c63 100644
--- a/crates/toml_edit/src/parser/array.rs
+++ b/crates/toml_edit/src/parser/array.rs
@@ -1,29 +1,31 @@
 use winnow::combinator::cut_err;
 use winnow::combinator::delimited;
 use winnow::combinator::opt;
-use winnow::combinator::separated1;
-use winnow::trace::trace;
+use winnow::combinator::peek;
+use winnow::combinator::separated;
+use winnow::combinator::trace;
 
 use crate::parser::trivia::ws_comment_newline;
 use crate::parser::value::value;
-use crate::{Array, Item, RawString, Value};
+use crate::{Array, Item, RawString};
 
 use crate::parser::prelude::*;
 
 // ;; Array
 
 // array = array-open array-values array-close
-pub(crate) fn array<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, Array, ContextError> {
+pub(crate) fn array<'i>(input: &mut Input<'i>) -> PResult<Array> {
     trace("array", move |input: &mut Input<'i>| {
         delimited(
             ARRAY_OPEN,
-            cut_err(array_values(check)),
+            cut_err(array_values),
             cut_err(ARRAY_CLOSE)
                 .context(StrContext::Label("array"))
                 .context(StrContext::Expected(StrContextValue::CharLiteral(']'))),
         )
         .parse_next(input)
     })
+    .parse_next(input)
 }
 
 // note: we're omitting ws and newlines here, because
@@ -35,49 +37,33 @@
 // array-sep = ws %x2C ws  ; , Comma
 const ARRAY_SEP: u8 = b',';
 
-// note: this rule is modified
-// array-values = [ ( array-value array-sep array-values ) /
-//                  array-value / ws-comment-newline ]
-pub(crate) fn array_values<'i>(
-    check: RecursionCheck,
-) -> impl Parser<Input<'i>, Array, ContextError> {
-    move |input: &mut Input<'i>| {
-        let check = check.recursing(input)?;
-        (
-            opt(
-                (separated1(array_value(check), ARRAY_SEP), opt(ARRAY_SEP)).map(
-                    |(v, trailing): (Vec<Value>, Option<u8>)| {
-                        (
-                            Array::with_vec(v.into_iter().map(Item::Value).collect()),
-                            trailing.is_some(),
-                        )
-                    },
-                ),
-            ),
-            ws_comment_newline.span(),
-        )
-            .try_map::<_, _, std::str::Utf8Error>(|(array, trailing)| {
-                let (mut array, comma) = array.unwrap_or_default();
-                array.set_trailing_comma(comma);
-                array.set_trailing(RawString::with_span(trailing));
-                Ok(array)
-            })
-            .parse_next(input)
+// array-values =  ws-comment-newline val ws-comment-newline array-sep array-values
+// array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]
+pub(crate) fn array_values(input: &mut Input<'_>) -> PResult<Array> {
+    if peek(opt(ARRAY_CLOSE)).parse_next(input)?.is_some() {
+        // Optimize for empty arrays, avoiding `value` from being expected to fail
+        return Ok(Array::new());
     }
+
+    let array = separated(0.., array_value, ARRAY_SEP).parse_next(input)?;
+    let mut array = Array::with_vec(array);
+    if !array.is_empty() {
+        let comma = opt(ARRAY_SEP).parse_next(input)?.is_some();
+        array.set_trailing_comma(comma);
+    }
+    let trailing = ws_comment_newline.span().parse_next(input)?;
+    array.set_trailing(RawString::with_span(trailing));
+
+    Ok(array)
 }
 
-pub(crate) fn array_value<'i>(
-    check: RecursionCheck,
-) -> impl Parser<Input<'i>, Value, ContextError> {
-    move |input: &mut Input<'i>| {
-        (
-            ws_comment_newline.span(),
-            value(check),
-            ws_comment_newline.span(),
-        )
-            .map(|(ws1, v, ws2)| v.decorated(RawString::with_span(ws1), RawString::with_span(ws2)))
-            .parse_next(input)
-    }
+pub(crate) fn array_value(input: &mut Input<'_>) -> PResult<Item> {
+    let prefix = ws_comment_newline.span().parse_next(input)?;
+    let value = value.parse_next(input)?;
+    let suffix = ws_comment_newline.span().parse_next(input)?;
+    let value = value.decorated(RawString::with_span(prefix), RawString::with_span(suffix));
+    let value = Item::Value(value);
+    Ok(value)
 }
 
 #[cfg(test)]
@@ -125,7 +111,7 @@
         ];
         for input in inputs {
             dbg!(input);
-            let mut parsed = array(Default::default()).parse(new_input(input));
+            let mut parsed = array.parse(new_input(input));
             if let Ok(parsed) = &mut parsed {
                 parsed.despan(input);
             }
@@ -138,7 +124,7 @@
         let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#];
         for input in invalid_inputs {
             dbg!(input);
-            let mut parsed = array(Default::default()).parse(new_input(input));
+            let mut parsed = array.parse(new_input(input));
             if let Ok(parsed) = &mut parsed {
                 parsed.despan(input);
             }
diff --git a/crates/toml_edit/src/parser/datetime.rs b/crates/toml_edit/src/parser/datetime.rs
index 945dc69..69c8d7f 100644
--- a/crates/toml_edit/src/parser/datetime.rs
+++ b/crates/toml_edit/src/parser/datetime.rs
@@ -4,15 +4,15 @@
 use crate::parser::prelude::*;
 use crate::parser::trivia::from_utf8_unchecked;
 
-use toml_datetime::*;
+use toml_datetime::{Date, Datetime, Offset, Time};
 use winnow::combinator::alt;
 use winnow::combinator::cut_err;
 use winnow::combinator::opt;
 use winnow::combinator::preceded;
+use winnow::combinator::trace;
 use winnow::stream::Stream as _;
 use winnow::token::one_of;
 use winnow::token::take_while;
-use winnow::trace::trace;
 
 // ;; Date and Time (as defined in RFC 3339)
 
@@ -73,7 +73,7 @@
         _ => 31,
     };
     if max_days_in_month < day {
-        input.reset(day_start);
+        input.reset(&day_start);
         return Err(winnow::error::ErrMode::from_external_error(
             input,
             winnow::error::ErrorKind::Verify,
diff --git a/crates/toml_edit/src/parser/document.rs b/crates/toml_edit/src/parser/document.rs
index aa8fb11..bff40bf 100644
--- a/crates/toml_edit/src/parser/document.rs
+++ b/crates/toml_edit/src/parser/document.rs
@@ -5,11 +5,10 @@
 use winnow::combinator::opt;
 use winnow::combinator::peek;
 use winnow::combinator::repeat;
+use winnow::combinator::trace;
 use winnow::token::any;
 use winnow::token::one_of;
-use winnow::trace::trace;
 
-use crate::document::Document;
 use crate::key::Key;
 use crate::parser::inline_table::KEYVAL_SEP;
 use crate::parser::key::key;
@@ -30,31 +29,28 @@
 //                ( ws keyval ws [ comment ] ) /
 //                ( ws table ws [ comment ] ) /
 //                  ws )
-pub(crate) fn document(input: &mut Input<'_>) -> PResult<Document> {
-    let state = RefCell::new(ParseState::default());
-    let state_ref = &state;
-
-    let _o = (
-        // Remove BOM if present
-        opt(b"\xEF\xBB\xBF"),
-        parse_ws(state_ref),
-        repeat(0.., (
-            dispatch! {peek(any);
-                crate::parser::trivia::COMMENT_START_SYMBOL => cut_err(parse_comment(state_ref)),
-                crate::parser::table::STD_TABLE_OPEN => cut_err(table(state_ref)),
-                crate::parser::trivia::LF |
-                crate::parser::trivia::CR => parse_newline(state_ref),
-                _ => cut_err(keyval(state_ref)),
-            },
+pub(crate) fn document<'s, 'i>(
+    state_ref: &'s RefCell<ParseState>,
+) -> impl Parser<Input<'i>, (), ContextError> + 's {
+    move |i: &mut Input<'i>| {
+        (
+            // Remove BOM if present
+            opt(b"\xEF\xBB\xBF"),
             parse_ws(state_ref),
-        ))
-        .map(|()| ()),
-        eof,
-    )
-        .parse_next(input)?;
-    state.into_inner().into_document().map_err(|err| {
-        winnow::error::ErrMode::from_external_error(input, winnow::error::ErrorKind::Verify, err)
-    })
+            repeat(0.., (
+                dispatch! {peek(any);
+                    crate::parser::trivia::COMMENT_START_SYMBOL => cut_err(parse_comment(state_ref)),
+                    crate::parser::table::STD_TABLE_OPEN => cut_err(table(state_ref)),
+                    crate::parser::trivia::LF |
+                    crate::parser::trivia::CR => parse_newline(state_ref),
+                    _ => cut_err(keyval(state_ref)),
+                },
+                parse_ws(state_ref),
+            ))
+            .map(|()| ()),
+            eof,
+        ).void().parse_next(i)
+    }
 }
 
 pub(crate) fn parse_comment<'s, 'i>(
@@ -113,7 +109,7 @@
                     .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
                 (
                     ws.span(),
-                    value(RecursionCheck::default()),
+                    value,
                     line_trailing
                         .context(StrContext::Expected(StrContextValue::CharLiteral('\n')))
                         .context(StrContext::Expected(StrContextValue::CharLiteral('#'))),
diff --git a/crates/toml_edit/src/parser/inline_table.rs b/crates/toml_edit/src/parser/inline_table.rs
index c2e6619..bba5100 100644
--- a/crates/toml_edit/src/parser/inline_table.rs
+++ b/crates/toml_edit/src/parser/inline_table.rs
@@ -1,8 +1,8 @@
 use winnow::combinator::cut_err;
 use winnow::combinator::delimited;
-use winnow::combinator::separated0;
+use winnow::combinator::separated;
+use winnow::combinator::trace;
 use winnow::token::one_of;
-use winnow::trace::trace;
 
 use crate::key::Key;
 use crate::parser::error::CustomError;
@@ -18,19 +18,18 @@
 // ;; Inline Table
 
 // inline-table = inline-table-open inline-table-keyvals inline-table-close
-pub(crate) fn inline_table<'i>(
-    check: RecursionCheck,
-) -> impl Parser<Input<'i>, InlineTable, ContextError> {
+pub(crate) fn inline_table<'i>(input: &mut Input<'i>) -> PResult<InlineTable> {
     trace("inline-table", move |input: &mut Input<'i>| {
         delimited(
             INLINE_TABLE_OPEN,
-            cut_err(inline_table_keyvals(check).try_map(|(kv, p)| table_from_pairs(kv, p))),
+            cut_err(inline_table_keyvals.try_map(|(kv, p)| table_from_pairs(kv, p))),
             cut_err(INLINE_TABLE_CLOSE)
                 .context(StrContext::Label("inline table"))
                 .context(StrContext::Expected(StrContextValue::CharLiteral('}'))),
         )
         .parse_next(input)
     })
+    .parse_next(input)
 }
 
 fn table_from_pairs(
@@ -118,50 +117,43 @@
 // ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
 // ( key keyval-sep val )
 
-fn inline_table_keyvals<'i>(
-    check: RecursionCheck,
-) -> impl Parser<Input<'i>, (Vec<(Vec<Key>, TableKeyValue)>, RawString), ContextError> {
-    move |input: &mut Input<'i>| {
-        let check = check.recursing(input)?;
-        (
-            separated0(keyval(check), INLINE_TABLE_SEP),
-            ws.span().map(RawString::with_span),
-        )
-            .parse_next(input)
-    }
+fn inline_table_keyvals(
+    input: &mut Input<'_>,
+) -> PResult<(Vec<(Vec<Key>, TableKeyValue)>, RawString)> {
+    (
+        separated(0.., keyval, INLINE_TABLE_SEP),
+        ws.span().map(RawString::with_span),
+    )
+        .parse_next(input)
 }
 
-fn keyval<'i>(
-    check: RecursionCheck,
-) -> impl Parser<Input<'i>, (Vec<Key>, TableKeyValue), ContextError> {
-    move |input: &mut Input<'i>| {
-        (
-            key,
-            cut_err((
-                one_of(KEYVAL_SEP)
-                    .context(StrContext::Expected(StrContextValue::CharLiteral('.')))
-                    .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
-                (ws.span(), value(check), ws.span()),
-            )),
-        )
-            .map(|(key, (_, v))| {
-                let mut path = key;
-                let key = path.pop().expect("grammar ensures at least 1");
+fn keyval(input: &mut Input<'_>) -> PResult<(Vec<Key>, TableKeyValue)> {
+    (
+        key,
+        cut_err((
+            one_of(KEYVAL_SEP)
+                .context(StrContext::Expected(StrContextValue::CharLiteral('.')))
+                .context(StrContext::Expected(StrContextValue::CharLiteral('='))),
+            (ws.span(), value, ws.span()),
+        )),
+    )
+        .map(|(key, (_, v))| {
+            let mut path = key;
+            let key = path.pop().expect("grammar ensures at least 1");
 
-                let (pre, v, suf) = v;
-                let pre = RawString::with_span(pre);
-                let suf = RawString::with_span(suf);
-                let v = v.decorated(pre, suf);
-                (
-                    path,
-                    TableKeyValue {
-                        key,
-                        value: Item::Value(v),
-                    },
-                )
-            })
-            .parse_next(input)
-    }
+            let (pre, v, suf) = v;
+            let pre = RawString::with_span(pre);
+            let suf = RawString::with_span(suf);
+            let v = v.decorated(pre, suf);
+            (
+                path,
+                TableKeyValue {
+                    key,
+                    value: Item::Value(v),
+                },
+            )
+        })
+        .parse_next(input)
 }
 
 #[cfg(test)]
@@ -181,7 +173,7 @@
         ];
         for input in inputs {
             dbg!(input);
-            let mut parsed = inline_table(Default::default()).parse(new_input(input));
+            let mut parsed = inline_table.parse(new_input(input));
             if let Ok(parsed) = &mut parsed {
                 parsed.despan(input);
             }
@@ -194,7 +186,7 @@
         let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
         for input in invalid_inputs {
             dbg!(input);
-            let mut parsed = inline_table(Default::default()).parse(new_input(input));
+            let mut parsed = inline_table.parse(new_input(input));
             if let Ok(parsed) = &mut parsed {
                 parsed.despan(input);
             }
diff --git a/crates/toml_edit/src/parser/key.rs b/crates/toml_edit/src/parser/key.rs
index e72b195..ee729fd 100644
--- a/crates/toml_edit/src/parser/key.rs
+++ b/crates/toml_edit/src/parser/key.rs
@@ -1,10 +1,10 @@
 use std::ops::RangeInclusive;
 
 use winnow::combinator::peek;
-use winnow::combinator::separated1;
+use winnow::combinator::separated;
+use winnow::combinator::trace;
 use winnow::token::any;
 use winnow::token::take_while;
-use winnow::trace::trace;
 
 use crate::key::Key;
 use crate::parser::error::CustomError;
@@ -20,7 +20,8 @@
 pub(crate) fn key(input: &mut Input<'_>) -> PResult<Vec<Key>> {
     let mut key_path = trace(
         "dotted-key",
-        separated1(
+        separated(
+            1..,
             (ws.span(), simple_key, ws.span()).map(|(pre, (raw, key), suffix)| {
                 Key::new(key)
                     .with_repr_unchecked(Repr::new_unchecked(raw))
diff --git a/crates/toml_edit/src/parser/mod.rs b/crates/toml_edit/src/parser/mod.rs
index e032202..1d5dbcf 100644
--- a/crates/toml_edit/src/parser/mod.rs
+++ b/crates/toml_edit/src/parser/mod.rs
@@ -1,5 +1,6 @@
 #![allow(clippy::type_complexity)]
 
+use std::cell::RefCell;
 pub(crate) mod array;
 pub(crate) mod datetime;
 pub(crate) mod document;
@@ -13,17 +14,21 @@
 pub(crate) mod trivia;
 pub(crate) mod value;
 
-pub use crate::error::TomlError;
+pub(crate) use crate::error::TomlError;
 
-pub(crate) fn parse_document(raw: &str) -> Result<crate::Document, TomlError> {
+pub(crate) fn parse_document<S: AsRef<str>>(raw: S) -> Result<crate::ImDocument<S>, TomlError> {
     use prelude::*;
 
-    let b = new_input(raw);
-    let mut doc = document::document
-        .parse(b)
+    let b = new_input(raw.as_ref());
+    let state = RefCell::new(state::ParseState::new());
+    let state_ref = &state;
+    document::document(state_ref)
+        .parse(b.clone())
         .map_err(|e| TomlError::new(e, b))?;
-    doc.span = Some(0..(raw.len()));
-    doc.original = Some(raw.to_owned());
+    let doc = state
+        .into_inner()
+        .into_document(raw)
+        .map_err(|e| TomlError::custom(e.to_string(), None))?;
     Ok(doc)
 }
 
@@ -31,7 +36,7 @@
     use prelude::*;
 
     let b = new_input(raw);
-    let result = key::simple_key.parse(b);
+    let result = key::simple_key.parse(b.clone());
     match result {
         Ok((raw, key)) => {
             Ok(crate::Key::new(key).with_repr_unchecked(crate::Repr::new_unchecked(raw)))
@@ -44,7 +49,7 @@
     use prelude::*;
 
     let b = new_input(raw);
-    let result = key::key.parse(b);
+    let result = key::key.parse(b.clone());
     match result {
         Ok(mut keys) => {
             for key in &mut keys {
@@ -60,7 +65,7 @@
     use prelude::*;
 
     let b = new_input(raw);
-    let parsed = value::value(RecursionCheck::default()).parse(b);
+    let parsed = value::value.parse(b.clone());
     match parsed {
         Ok(mut value) => {
             // Only take the repr and not decor, as its probably not intended
@@ -81,60 +86,68 @@
     pub(crate) use winnow::PResult;
     pub(crate) use winnow::Parser;
 
-    pub(crate) type Input<'b> = winnow::Located<&'b winnow::BStr>;
+    pub(crate) type Input<'b> = winnow::Stateful<winnow::Located<&'b winnow::BStr>, RecursionCheck>;
 
     pub(crate) fn new_input(s: &str) -> Input<'_> {
-        winnow::Located::new(winnow::BStr::new(s))
+        winnow::Stateful {
+            input: winnow::Located::new(winnow::BStr::new(s)),
+            state: Default::default(),
+        }
     }
 
-    #[cfg(not(feature = "unbounded"))]
-    #[derive(Copy, Clone, Debug, Default)]
+    #[derive(Clone, Debug, Default, PartialEq, Eq)]
     pub(crate) struct RecursionCheck {
+        #[cfg(not(feature = "unbounded"))]
         current: usize,
     }
 
     #[cfg(not(feature = "unbounded"))]
+    const LIMIT: usize = 80;
+
     impl RecursionCheck {
-        pub(crate) fn check_depth(depth: usize) -> Result<(), super::error::CustomError> {
-            if depth < 128 {
-                Ok(())
-            } else {
-                Err(super::error::CustomError::RecursionLimitExceeded)
+        pub(crate) fn check_depth(_depth: usize) -> Result<(), super::error::CustomError> {
+            #[cfg(not(feature = "unbounded"))]
+            if LIMIT <= _depth {
+                return Err(super::error::CustomError::RecursionLimitExceeded);
             }
+
+            Ok(())
         }
 
-        pub(crate) fn recursing(
-            mut self,
-            input: &mut Input<'_>,
-        ) -> Result<Self, winnow::error::ErrMode<ContextError>> {
-            self.current += 1;
-            if self.current < 128 {
-                Ok(self)
-            } else {
-                Err(winnow::error::ErrMode::from_external_error(
-                    input,
-                    winnow::error::ErrorKind::Eof,
-                    super::error::CustomError::RecursionLimitExceeded,
-                ))
+        fn enter(&mut self) -> Result<(), super::error::CustomError> {
+            #[cfg(not(feature = "unbounded"))]
+            {
+                self.current += 1;
+                if LIMIT <= self.current {
+                    return Err(super::error::CustomError::RecursionLimitExceeded);
+                }
+            }
+            Ok(())
+        }
+
+        fn exit(&mut self) {
+            #[cfg(not(feature = "unbounded"))]
+            {
+                self.current -= 1;
             }
         }
     }
 
-    #[cfg(feature = "unbounded")]
-    #[derive(Copy, Clone, Debug, Default)]
-    pub(crate) struct RecursionCheck {}
-
-    #[cfg(feature = "unbounded")]
-    impl RecursionCheck {
-        pub(crate) fn check_depth(_depth: usize) -> Result<(), super::error::CustomError> {
-            Ok(())
-        }
-
-        pub(crate) fn recursing(
-            self,
-            _input: &mut Input<'_>,
-        ) -> Result<Self, winnow::error::ErrMode<ContextError>> {
-            Ok(self)
+    pub(crate) fn check_recursion<'b, O>(
+        mut parser: impl Parser<Input<'b>, O, ContextError>,
+    ) -> impl Parser<Input<'b>, O, ContextError> {
+        move |input: &mut Input<'b>| {
+            input.state.enter().map_err(|err| {
+                winnow::error::ErrMode::from_external_error(
+                    input,
+                    winnow::error::ErrorKind::Eof,
+                    err,
+                )
+                .cut()
+            })?;
+            let result = parser.parse_next(input);
+            input.state.exit();
+            result
         }
     }
 }
@@ -144,6 +157,8 @@
 #[cfg(feature = "display")]
 mod test {
     use super::*;
+    use snapbox::assert_data_eq;
+    use snapbox::prelude::*;
 
     #[test]
     fn documents() {
@@ -207,10 +222,7 @@
         ];
         for input in documents {
             dbg!(input);
-            let mut parsed = parse_document(input);
-            if let Ok(parsed) = &mut parsed {
-                parsed.despan();
-            }
+            let parsed = parse_document(input).map(|d| d.into_mut());
             let doc = match parsed {
                 Ok(doc) => doc,
                 Err(err) => {
@@ -221,7 +233,7 @@
                 }
             };
 
-            snapbox::assert_eq(input, doc.to_string());
+            assert_data_eq!(doc.to_string(), input.raw());
         }
     }
 
@@ -235,10 +247,7 @@
 "];
         for input in parse_only {
             dbg!(input);
-            let mut parsed = parse_document(input);
-            if let Ok(parsed) = &mut parsed {
-                parsed.despan();
-            }
+            let parsed = parse_document(input).map(|d| d.into_mut());
             match parsed {
                 Ok(_) => (),
                 Err(err) => {
@@ -257,10 +266,7 @@
 $"#];
         for input in invalid_inputs {
             dbg!(input);
-            let mut parsed = parse_document(input);
-            if let Ok(parsed) = &mut parsed {
-                parsed.despan();
-            }
+            let parsed = parse_document(input).map(|d| d.into_mut());
             assert!(parsed.is_err(), "Input: {:?}", input);
         }
     }
diff --git a/crates/toml_edit/src/parser/numbers.rs b/crates/toml_edit/src/parser/numbers.rs
index 9681526..2310f24 100644
--- a/crates/toml_edit/src/parser/numbers.rs
+++ b/crates/toml_edit/src/parser/numbers.rs
@@ -7,10 +7,9 @@
 use winnow::combinator::preceded;
 use winnow::combinator::repeat;
 use winnow::combinator::rest;
+use winnow::combinator::trace;
 use winnow::token::one_of;
-use winnow::token::tag;
 use winnow::token::take;
-use winnow::trace::trace;
 
 use crate::parser::prelude::*;
 use crate::parser::trivia::from_utf8_unchecked;
@@ -63,23 +62,23 @@
                     repeat(
                         0..,
                         alt((
-                            digit.value(()),
+                            digit.void(),
                             (
                                 one_of(b'_'),
                                 cut_err(digit).context(StrContext::Expected(
                                     StrContextValue::Description("digit"),
                                 )),
                             )
-                                .value(()),
+                                .void(),
                         )),
                     )
                     .map(|()| ()),
                 )
-                    .value(()),
-                digit.value(()),
+                    .void(),
+                digit.void(),
             )),
         )
-            .recognize()
+            .take()
             .map(|b: &[u8]| unsafe {
                 from_utf8_unchecked(b, "`digit` and `_` filter out non-ASCII")
             })
@@ -101,19 +100,19 @@
                 repeat(
                     0..,
                     alt((
-                        hexdig.value(()),
+                        hexdig.void(),
                         (
                             one_of(b'_'),
                             cut_err(hexdig).context(StrContext::Expected(
                                 StrContextValue::Description("digit"),
                             )),
                         )
-                            .value(()),
+                            .void(),
                     )),
                 )
                 .map(|()| ()),
             ))
-            .recognize(),
+            .take(),
         )
         .map(|b| unsafe { from_utf8_unchecked(b, "`hexdig` and `_` filter out non-ASCII") })
         .context(StrContext::Label("hexadecimal integer")),
@@ -134,19 +133,19 @@
                 repeat(
                     0..,
                     alt((
-                        one_of(DIGIT0_7).value(()),
+                        one_of(DIGIT0_7).void(),
                         (
                             one_of(b'_'),
                             cut_err(one_of(DIGIT0_7)).context(StrContext::Expected(
                                 StrContextValue::Description("digit"),
                             )),
                         )
-                            .value(()),
+                            .void(),
                     )),
                 )
                 .map(|()| ()),
             ))
-            .recognize(),
+            .take(),
         )
         .map(|b| unsafe { from_utf8_unchecked(b, "`DIGIT0_7` and `_` filter out non-ASCII") })
         .context(StrContext::Label("octal integer")),
@@ -168,19 +167,19 @@
                 repeat(
                     0..,
                     alt((
-                        one_of(DIGIT0_1).value(()),
+                        one_of(DIGIT0_1).void(),
                         (
                             one_of(b'_'),
                             cut_err(one_of(DIGIT0_1)).context(StrContext::Expected(
                                 StrContextValue::Description("digit"),
                             )),
                         )
-                            .value(()),
+                            .void(),
                     )),
                 )
                 .map(|()| ()),
             ))
-            .recognize(),
+            .take(),
         )
         .map(|b| unsafe { from_utf8_unchecked(b, "`DIGIT0_1` and `_` filter out non-ASCII") })
         .context(StrContext::Label("binary integer")),
@@ -215,7 +214,7 @@
         dec_int,
         alt((exp.void(), (frac.void(), opt(exp.void())).void())),
     )
-        .recognize()
+        .take()
         .map(|b: &[u8]| unsafe {
             from_utf8_unchecked(
                 b,
@@ -233,7 +232,7 @@
         cut_err(zero_prefixable_int)
             .context(StrContext::Expected(StrContextValue::Description("digit"))),
     )
-        .recognize()
+        .take()
         .map(|b: &[u8]| unsafe {
             from_utf8_unchecked(
                 b,
@@ -250,18 +249,18 @@
         repeat(
             0..,
             alt((
-                digit.value(()),
+                digit.void(),
                 (
                     one_of(b'_'),
                     cut_err(digit)
                         .context(StrContext::Expected(StrContextValue::Description("digit"))),
                 )
-                    .value(()),
+                    .void(),
             )),
         )
         .map(|()| ()),
     )
-        .recognize()
+        .take()
         .map(|b: &[u8]| unsafe { from_utf8_unchecked(b, "`digit` and `_` filter out non-ASCII") })
         .parse_next(input)
 }
@@ -274,7 +273,7 @@
         opt(one_of([b'+', b'-'])),
         cut_err(zero_prefixable_int),
     )
-        .recognize()
+        .take()
         .map(|b: &[u8]| unsafe {
             from_utf8_unchecked(
                 b,
@@ -296,12 +295,12 @@
 }
 // inf = %x69.6e.66  ; inf
 pub(crate) fn inf(input: &mut Input<'_>) -> PResult<f64> {
-    tag(INF).value(f64::INFINITY).parse_next(input)
+    INF.value(f64::INFINITY).parse_next(input)
 }
 const INF: &[u8] = b"inf";
 // nan = %x6e.61.6e  ; nan
 pub(crate) fn nan(input: &mut Input<'_>) -> PResult<f64> {
-    tag(NAN).value(f64::NAN.copysign(1.0)).parse_next(input)
+    NAN.value(f64::NAN.copysign(1.0)).parse_next(input)
 }
 const NAN: &[u8] = b"nan";
 
@@ -337,8 +336,8 @@
             ("0xF", 15),
             ("0o0_755", 493),
             ("0b1_0_1", 5),
-            (&std::i64::MIN.to_string()[..], std::i64::MIN),
-            (&std::i64::MAX.to_string()[..], std::i64::MAX),
+            (&i64::MIN.to_string()[..], i64::MIN),
+            (&i64::MAX.to_string()[..], i64::MAX),
         ];
         for &(input, expected) in &cases {
             dbg!(input);
@@ -362,7 +361,7 @@
         } else {
             dbg!(expected);
             dbg!(actual);
-            assert!((expected - actual).abs() < std::f64::EPSILON);
+            assert!((expected - actual).abs() < f64::EPSILON);
         }
     }
 
@@ -377,15 +376,15 @@
             ("-2E-2", -2E-2),
             ("6.626e-34", 6.626e-34),
             ("9_224_617.445_991_228_313", 9_224_617.445_991_227),
-            ("-1.7976931348623157e+308", std::f64::MIN),
-            ("1.7976931348623157e+308", std::f64::MAX),
+            ("-1.7976931348623157e+308", f64::MIN),
+            ("1.7976931348623157e+308", f64::MAX),
             ("nan", f64::NAN.copysign(1.0)),
             ("+nan", f64::NAN.copysign(1.0)),
             ("-nan", f64::NAN.copysign(-1.0)),
             ("inf", f64::INFINITY),
             ("+inf", f64::INFINITY),
             ("-inf", f64::NEG_INFINITY),
-            // ("1e+400", std::f64::INFINITY),
+            // ("1e+400", f64::INFINITY),
         ];
         for &(input, expected) in &cases {
             dbg!(input);
diff --git a/crates/toml_edit/src/parser/state.rs b/crates/toml_edit/src/parser/state.rs
index 187dd5f..2513634 100644
--- a/crates/toml_edit/src/parser/state.rs
+++ b/crates/toml_edit/src/parser/state.rs
@@ -2,10 +2,10 @@
 use crate::parser::error::CustomError;
 use crate::repr::Decor;
 use crate::table::TableKeyValue;
-use crate::{ArrayOfTables, Document, InternalString, Item, RawString, Table};
+use crate::{ArrayOfTables, ImDocument, InternalString, Item, RawString, Table};
 
 pub(crate) struct ParseState {
-    document: Document,
+    root: Table,
     trailing: Option<std::ops::Range<usize>>,
     current_table_position: usize,
     current_table: Table,
@@ -14,11 +14,27 @@
 }
 
 impl ParseState {
-    pub(crate) fn into_document(mut self) -> Result<Document, CustomError> {
+    pub(crate) fn new() -> Self {
+        let mut root = Table::new();
+        root.span = Some(0..0);
+        Self {
+            root: Table::new(),
+            trailing: None,
+            current_table_position: 0,
+            current_table: root,
+            current_is_array: false,
+            current_table_path: Vec::new(),
+        }
+    }
+
+    pub(crate) fn into_document<S>(mut self, raw: S) -> Result<ImDocument<S>, CustomError> {
         self.finalize_table()?;
-        let trailing = self.trailing.map(RawString::with_span);
-        self.document.trailing = trailing.unwrap_or_default();
-        Ok(self.document)
+        let trailing = self.trailing.map(RawString::with_span).unwrap_or_default();
+        Ok(ImDocument {
+            root: Item::Table(self.root),
+            trailing,
+            raw,
+        })
     }
 
     pub(crate) fn on_ws(&mut self, span: std::ops::Range<usize>) {
@@ -100,7 +116,7 @@
         debug_assert!(self.current_table_path.is_empty());
 
         // Look up the table on start to ensure the duplicate_key error points to the right line
-        let root = self.document.as_table_mut();
+        let root = &mut self.root;
         let parent_table = Self::descend_path(root, &path[..path.len() - 1], false)?;
         let key = &path[path.len() - 1];
         let entry = parent_table
@@ -134,7 +150,7 @@
 
         // 1. Look up the table on start to ensure the duplicate_key error points to the right line
         // 2. Ensure any child tables from an implicit table are preserved
-        let root = self.document.as_table_mut();
+        let root = &mut self.root;
         let parent_table = Self::descend_path(root, &path[..path.len() - 1], false)?;
         let key = &path[path.len() - 1];
         if let Some(entry) = parent_table.remove(key.get()) {
@@ -163,7 +179,7 @@
         let mut table = std::mem::take(&mut self.current_table);
         let path = std::mem::take(&mut self.current_table_path);
 
-        let root = self.document.as_table_mut();
+        let root = &mut self.root;
         if path.is_empty() {
             assert!(root.is_empty());
             std::mem::swap(&mut table, root);
@@ -249,7 +265,7 @@
                     }
                     table = sweet_child_of_mine;
                 }
-                _ => unreachable!(),
+                Item::None => unreachable!(),
             }
         }
         Ok(table)
@@ -301,18 +317,3 @@
         Ok(())
     }
 }
-
-impl Default for ParseState {
-    fn default() -> Self {
-        let mut root = Table::new();
-        root.span = Some(0..0);
-        Self {
-            document: Document::new(),
-            trailing: None,
-            current_table_position: 0,
-            current_table: root,
-            current_is_array: false,
-            current_table_path: Vec::new(),
-        }
-    }
-}
diff --git a/crates/toml_edit/src/parser/strings.rs b/crates/toml_edit/src/parser/strings.rs
index 675b5c6..b1c41b9 100644
--- a/crates/toml_edit/src/parser/strings.rs
+++ b/crates/toml_edit/src/parser/strings.rs
@@ -5,21 +5,20 @@
 use winnow::combinator::alt;
 use winnow::combinator::cut_err;
 use winnow::combinator::delimited;
+use winnow::combinator::empty;
 use winnow::combinator::fail;
 use winnow::combinator::opt;
 use winnow::combinator::peek;
 use winnow::combinator::preceded;
 use winnow::combinator::repeat;
-use winnow::combinator::success;
 use winnow::combinator::terminated;
+use winnow::combinator::trace;
 use winnow::prelude::*;
 use winnow::stream::Stream;
 use winnow::token::any;
 use winnow::token::none_of;
 use winnow::token::one_of;
-use winnow::token::tag;
 use winnow::token::take_while;
-use winnow::trace::trace;
 
 use crate::parser::error::CustomError;
 use crate::parser::numbers::HEXDIG;
@@ -110,15 +109,15 @@
 // escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX            U+XXXXXXXX
 fn escape_seq_char(input: &mut Input<'_>) -> PResult<char> {
     dispatch! {any;
-        b'b' => success('\u{8}'),
-        b'f' => success('\u{c}'),
-        b'n' => success('\n'),
-        b'r' => success('\r'),
-        b't' => success('\t'),
+        b'b' => empty.value('\u{8}'),
+        b'f' => empty.value('\u{c}'),
+        b'n' => empty.value('\n'),
+        b'r' => empty.value('\r'),
+        b't' => empty.value('\t'),
         b'u' => cut_err(hexescape::<4>).context(StrContext::Label("unicode 4-digit hex code")),
         b'U' => cut_err(hexescape::<8>).context(StrContext::Label("unicode 8-digit hex code")),
-        b'\\' => success('\\'),
-        b'"' => success('"'),
+        b'\\' => empty.value('\\'),
+        b'"' => empty.value('"'),
         _ => {
             cut_err(fail::<_, char, _>)
             .context(StrContext::Label("escape sequence"))
@@ -154,10 +153,10 @@
         "ml-basic-string",
         delimited(
             ML_BASIC_STRING_DELIM,
-            preceded(opt(newline), cut_err(ml_basic_body)),
-            cut_err(ML_BASIC_STRING_DELIM),
-        )
-        .context(StrContext::Label("multiline basic string")),
+            preceded(opt(newline), cut_err(ml_basic_body))
+                .context(StrContext::Label("multiline basic string")),
+            cut_err(ML_BASIC_STRING_DELIM).context(StrContext::Label("multiline basic string")),
+        ),
     )
     .parse_next(input)
 }
@@ -187,7 +186,7 @@
         }
     }
 
-    if let Some(qi) = opt(mlb_quotes(tag(ML_BASIC_STRING_DELIM).value(()))).parse_next(input)? {
+    if let Some(qi) = opt(mlb_quotes(ML_BASIC_STRING_DELIM.void())).parse_next(input)? {
         c.to_mut().push_str(qi);
     }
 
@@ -213,7 +212,7 @@
 
 // mlb-quotes = 1*2quotation-mark
 fn mlb_quotes<'i>(
-    mut term: impl winnow::Parser<Input<'i>, (), ContextError>,
+    mut term: impl Parser<Input<'i>, (), ContextError>,
 ) -> impl Parser<Input<'i>, &'i str, ContextError> {
     move |input: &mut Input<'i>| {
         let start = input.checkpoint();
@@ -223,7 +222,7 @@
 
         match res {
             Err(winnow::error::ErrMode::Backtrack(_)) => {
-                input.reset(start);
+                input.reset(&start);
                 terminated(b"\"", peek(term.by_ref()))
                     .map(|b| unsafe { from_utf8_unchecked(b, "`bytes` out non-ASCII") })
                     .parse_next(input)
@@ -297,10 +296,10 @@
                 } else {
                     Cow::Borrowed(t)
                 }
-            })),
-            cut_err(ML_LITERAL_STRING_DELIM),
-        )
-        .context(StrContext::Label("multiline literal string")),
+            }))
+            .context(StrContext::Label("multiline literal string")),
+            cut_err(ML_LITERAL_STRING_DELIM).context(StrContext::Label("multiline literal string")),
+        ),
     )
     .parse_next(input)
 }
@@ -320,16 +319,16 @@
             ),
         )
         .map(|()| ()),
-        opt(mll_quotes(tag(ML_LITERAL_STRING_DELIM).value(()))),
+        opt(mll_quotes(ML_LITERAL_STRING_DELIM.void())),
     )
-        .recognize()
+        .take()
         .try_map(std::str::from_utf8)
         .parse_next(input)
 }
 
 // mll-content = mll-char / newline
 fn mll_content(input: &mut Input<'_>) -> PResult<u8> {
-    alt((one_of(MLL_CHAR), newline)).parse_next(input)
+    alt((one_of(MLL_CHAR), newline.value(b'\n'))).parse_next(input)
 }
 
 // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
@@ -342,7 +341,7 @@
 
 // mll-quotes = 1*2apostrophe
 fn mll_quotes<'i>(
-    mut term: impl winnow::Parser<Input<'i>, (), ContextError>,
+    mut term: impl Parser<Input<'i>, (), ContextError>,
 ) -> impl Parser<Input<'i>, &'i str, ContextError> {
     move |input: &mut Input<'i>| {
         let start = input.checkpoint();
@@ -352,7 +351,7 @@
 
         match res {
             Err(winnow::error::ErrMode::Backtrack(_)) => {
-                input.reset(start);
+                input.reset(&start);
                 terminated(b"'", peek(term.by_ref()))
                     .map(|b| unsafe { from_utf8_unchecked(b, "`bytes` out non-ASCII") })
                     .parse_next(input)
diff --git a/crates/toml_edit/src/parser/trivia.rs b/crates/toml_edit/src/parser/trivia.rs
index 4575fb1..5630bca 100644
--- a/crates/toml_edit/src/parser/trivia.rs
+++ b/crates/toml_edit/src/parser/trivia.rs
@@ -1,11 +1,16 @@
 use std::ops::RangeInclusive;
 
 use winnow::combinator::alt;
+use winnow::combinator::empty;
 use winnow::combinator::eof;
+use winnow::combinator::fail;
 use winnow::combinator::opt;
+use winnow::combinator::peek;
 use winnow::combinator::repeat;
 use winnow::combinator::terminated;
 use winnow::prelude::*;
+use winnow::stream::Stream as _;
+use winnow::token::any;
 use winnow::token::one_of;
 use winnow::token::take_while;
 
@@ -15,11 +20,13 @@
     bytes: &'b [u8],
     safety_justification: &'static str,
 ) -> &'b str {
-    if cfg!(debug_assertions) {
-        // Catch problems more quickly when testing
-        std::str::from_utf8(bytes).expect(safety_justification)
-    } else {
-        std::str::from_utf8_unchecked(bytes)
+    unsafe {
+        if cfg!(debug_assertions) {
+            // Catch problems more quickly when testing
+            std::str::from_utf8(bytes).expect(safety_justification)
+        } else {
+            std::str::from_utf8_unchecked(bytes)
+        }
     }
 }
 
@@ -48,69 +55,71 @@
 pub(crate) const COMMENT_START_SYMBOL: u8 = b'#';
 
 // comment = comment-start-symbol *non-eol
-pub(crate) fn comment<'i>(input: &mut Input<'i>) -> PResult<&'i [u8]> {
+pub(crate) fn comment(input: &mut Input<'_>) -> PResult<()> {
     (COMMENT_START_SYMBOL, take_while(0.., NON_EOL))
-        .recognize()
+        .void()
         .parse_next(input)
 }
 
 // newline = ( %x0A /              ; LF
 //             %x0D.0A )           ; CRLF
-pub(crate) fn newline(input: &mut Input<'_>) -> PResult<u8> {
-    alt((
-        one_of(LF).value(b'\n'),
-        (one_of(CR), one_of(LF)).value(b'\n'),
-    ))
+pub(crate) fn newline(input: &mut Input<'_>) -> PResult<()> {
+    dispatch! {any;
+        b'\n' => empty,
+        b'\r' => one_of(LF).void(),
+        _ => fail,
+    }
     .parse_next(input)
 }
 pub(crate) const LF: u8 = b'\n';
 pub(crate) const CR: u8 = b'\r';
 
 // ws-newline       = *( wschar / newline )
-pub(crate) fn ws_newline<'i>(input: &mut Input<'i>) -> PResult<&'i str> {
+pub(crate) fn ws_newline(input: &mut Input<'_>) -> PResult<()> {
     repeat(
         0..,
         alt((newline.value(&b"\n"[..]), take_while(1.., WSCHAR))),
     )
     .map(|()| ())
-    .recognize()
-    .map(|b| unsafe { from_utf8_unchecked(b, "`is_wschar` and `newline` filters out on-ASCII") })
     .parse_next(input)
 }
 
 // ws-newlines      = newline *( wschar / newline )
-pub(crate) fn ws_newlines<'i>(input: &mut Input<'i>) -> PResult<&'i str> {
-    (newline, ws_newline)
-        .recognize()
-        .map(|b| unsafe {
-            from_utf8_unchecked(b, "`is_wschar` and `newline` filters out on-ASCII")
-        })
-        .parse_next(input)
+pub(crate) fn ws_newlines(input: &mut Input<'_>) -> PResult<()> {
+    (newline, ws_newline).void().parse_next(input)
 }
 
 // note: this rule is not present in the original grammar
 // ws-comment-newline = *( ws-newline-nonempty / comment )
-pub(crate) fn ws_comment_newline<'i>(input: &mut Input<'i>) -> PResult<&'i [u8]> {
-    repeat(
-        0..,
-        alt((
-            repeat(
-                1..,
-                alt((take_while(1.., WSCHAR), newline.value(&b"\n"[..]))),
-            )
-            .map(|()| ()),
-            comment.value(()),
-        )),
-    )
-    .map(|()| ())
-    .recognize()
-    .parse_next(input)
+pub(crate) fn ws_comment_newline(input: &mut Input<'_>) -> PResult<()> {
+    let mut start = input.checkpoint();
+    loop {
+        let _ = ws.parse_next(input)?;
+
+        let next_token = opt(peek(any)).parse_next(input)?;
+        match next_token {
+            Some(b'#') => (comment, newline).void().parse_next(input)?,
+            Some(b'\n') => (newline).void().parse_next(input)?,
+            Some(b'\r') => (newline).void().parse_next(input)?,
+            _ => break,
+        }
+
+        let end = input.checkpoint();
+        if start == end {
+            break;
+        }
+        start = end;
+    }
+
+    Ok(())
 }
 
 // note: this rule is not present in the original grammar
 // line-ending = newline / eof
-pub(crate) fn line_ending<'i>(input: &mut Input<'i>) -> PResult<&'i str> {
-    alt((newline.value("\n"), eof.value(""))).parse_next(input)
+pub(crate) fn line_ending(input: &mut Input<'_>) -> PResult<()> {
+    alt((newline.value("\n"), eof.value("")))
+        .void()
+        .parse_next(input)
 }
 
 // note: this rule is not present in the original grammar
@@ -149,7 +158,7 @@
         ];
         for input in inputs {
             dbg!(input);
-            let parsed = ws_comment_newline.parse(new_input(input));
+            let parsed = ws_comment_newline.take().parse(new_input(input));
             assert!(parsed.is_ok(), "{:?}", parsed);
             let parsed = parsed.unwrap();
             assert_eq!(parsed, input.as_bytes());
diff --git a/crates/toml_edit/src/parser/value.rs b/crates/toml_edit/src/parser/value.rs
index 33300ec..93f2eef 100644
--- a/crates/toml_edit/src/parser/value.rs
+++ b/crates/toml_edit/src/parser/value.rs
@@ -10,66 +10,64 @@
 use crate::parser::prelude::*;
 use crate::parser::strings::string;
 use crate::repr::{Formatted, Repr};
-use crate::value as v;
 use crate::RawString;
 use crate::Value;
 
 // val = string / boolean / array / inline-table / date-time / float / integer
-pub(crate) fn value<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, v::Value, ContextError> {
-    move |input: &mut Input<'i>| {
-        dispatch!{peek(any);
+pub(crate) fn value(input: &mut Input<'_>) -> PResult<Value> {
+    dispatch! {peek(any);
             crate::parser::strings::QUOTATION_MARK |
             crate::parser::strings::APOSTROPHE => string.map(|s| {
-                v::Value::String(Formatted::new(
+                Value::String(Formatted::new(
                     s.into_owned()
                 ))
             }),
-            crate::parser::array::ARRAY_OPEN => array(check).map(v::Value::Array),
-            crate::parser::inline_table::INLINE_TABLE_OPEN => inline_table(check).map(v::Value::InlineTable),
+            crate::parser::array::ARRAY_OPEN => check_recursion(array).map(Value::Array),
+            crate::parser::inline_table::INLINE_TABLE_OPEN => check_recursion(inline_table).map(Value::InlineTable),
             // Date/number starts
             b'+' | b'-' | b'0'..=b'9' => {
                 // Uncommon enough not to be worth optimizing at this time
                 alt((
                     date_time
-                        .map(v::Value::from),
+                        .map(Value::from),
                     float
-                        .map(v::Value::from),
+                        .map(Value::from),
                     integer
-                        .map(v::Value::from),
+                        .map(Value::from),
                 ))
             },
             // Report as if they were numbers because its most likely a typo
             b'_' => {
                     integer
-                        .map(v::Value::from)
+                        .map(Value::from)
                 .context(StrContext::Expected(StrContextValue::Description("leading digit")))
             },
             // Report as if they were numbers because its most likely a typo
             b'.' =>  {
                     float
-                        .map(v::Value::from)
+                        .map(Value::from)
                 .context(StrContext::Expected(StrContextValue::Description("leading digit")))
             },
             b't' => {
-                crate::parser::numbers::true_.map(v::Value::from)
+                crate::parser::numbers::true_.map(Value::from)
                     .context(StrContext::Label("string"))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
             },
             b'f' => {
-                crate::parser::numbers::false_.map(v::Value::from)
+                crate::parser::numbers::false_.map(Value::from)
                     .context(StrContext::Label("string"))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
             },
             b'i' => {
-                crate::parser::numbers::inf.map(v::Value::from)
+                crate::parser::numbers::inf.map(Value::from)
                     .context(StrContext::Label("string"))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
             },
             b'n' => {
-                crate::parser::numbers::nan.map(v::Value::from)
+                crate::parser::numbers::nan.map(Value::from)
                     .context(StrContext::Label("string"))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
@@ -81,13 +79,12 @@
                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
             },
     }
-        .with_span()
-        .try_map(|(value, span)| apply_raw(value, span))
-        .parse_next(input)
-    }
+    .with_span()
+    .map(|(value, span)| apply_raw(value, span))
+    .parse_next(input)
 }
 
-fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std::str::Utf8Error> {
+fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Value {
     match val {
         Value::String(ref mut f) => {
             let raw = RawString::with_span(span);
@@ -117,7 +114,7 @@
         }
     };
     val.decorate("", "");
-    Ok(val)
+    val
 }
 
 #[cfg(test)]
@@ -147,7 +144,7 @@
         ];
         for input in inputs {
             dbg!(input);
-            let mut parsed = value(Default::default()).parse(new_input(input));
+            let mut parsed = value.parse(new_input(input));
             if let Ok(parsed) = &mut parsed {
                 parsed.despan(input);
             }
diff --git a/crates/toml_edit/src/raw_string.rs b/crates/toml_edit/src/raw_string.rs
index 53714a1..0bde3b7 100644
--- a/crates/toml_edit/src/raw_string.rs
+++ b/crates/toml_edit/src/raw_string.rs
@@ -21,6 +21,8 @@
     }
 
     /// Access the underlying string
+    ///
+    /// This generally requires a [`DocumentMut`][crate::DocumentMut].
     pub fn as_str(&self) -> Option<&str> {
         match &self.0 {
             RawStringInner::Empty => Some(""),
@@ -29,6 +31,17 @@
         }
     }
 
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
+        match &self.0 {
+            RawStringInner::Empty => None,
+            RawStringInner::Explicit(_) => None,
+            RawStringInner::Spanned(span) => Some(span.clone()),
+        }
+    }
+
     pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
         match &self.0 {
             RawStringInner::Empty => "",
@@ -59,15 +72,6 @@
         }
     }
 
-    /// Access the underlying span
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
-        match &self.0 {
-            RawStringInner::Empty => None,
-            RawStringInner::Explicit(_) => None,
-            RawStringInner::Spanned(span) => Some(span.clone()),
-        }
-    }
-
     pub(crate) fn despan(&mut self, input: &str) {
         match &self.0 {
             RawStringInner::Empty => {}
@@ -75,7 +79,7 @@
             RawStringInner::Spanned(span) => {
                 *self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
                     panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
-                }))
+                }));
             }
         }
     }
diff --git a/crates/toml_edit/src/repr.rs b/crates/toml_edit/src/repr.rs
index ad41bbf..80d78c3 100644
--- a/crates/toml_edit/src/repr.rs
+++ b/crates/toml_edit/src/repr.rs
@@ -51,7 +51,7 @@
 
     /// Returns a raw representation.
     #[cfg(feature = "display")]
-    pub fn display_repr(&self) -> Cow<str> {
+    pub fn display_repr(&self) -> Cow<'_, str> {
         self.as_repr()
             .and_then(|r| r.as_raw().as_str())
             .map(Cow::Borrowed)
@@ -60,8 +60,10 @@
             })
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.repr.as_ref().and_then(|r| r.span())
     }
 
@@ -150,13 +152,15 @@
         &self.raw_value
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.raw_value.span()
     }
 
     pub(crate) fn despan(&mut self, input: &str) {
-        self.raw_value.despan(input)
+        self.raw_value.despan(input);
     }
 
     #[cfg(feature = "display")]
diff --git a/crates/toml_edit/src/ser/array.rs b/crates/toml_edit/src/ser/array.rs
index 80eba8b..a42cc66 100644
--- a/crates/toml_edit/src/ser/array.rs
+++ b/crates/toml_edit/src/ser/array.rs
@@ -21,9 +21,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         let value = value.serialize(super::ValueSerializer {})?;
         self.values.push(crate::Item::Value(value));
@@ -39,9 +39,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         serde::ser::SerializeSeq::serialize_element(self, value)
     }
@@ -55,9 +55,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         serde::ser::SerializeSeq::serialize_element(self, value)
     }
@@ -71,9 +71,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         serde::ser::SerializeSeq::serialize_element(self, value)
     }
diff --git a/crates/toml_edit/src/ser/key.rs b/crates/toml_edit/src/ser/key.rs
index d5e381b..3ba0840 100644
--- a/crates/toml_edit/src/ser/key.rs
+++ b/crates/toml_edit/src/ser/key.rs
@@ -75,9 +75,9 @@
         Err(Error::KeyNotString)
     }
 
-    fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<InternalString, Self::Error>
+    fn serialize_some<T>(self, _value: &T) -> Result<InternalString, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         Err(Error::KeyNotString)
     }
@@ -99,18 +99,18 @@
         Ok(variant.into())
     }
 
-    fn serialize_newtype_struct<T: ?Sized>(
+    fn serialize_newtype_struct<T>(
         self,
         _name: &'static str,
         value: &T,
     ) -> Result<InternalString, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         value.serialize(self)
     }
 
-    fn serialize_newtype_variant<T: ?Sized>(
+    fn serialize_newtype_variant<T>(
         self,
         _name: &'static str,
         _variant_index: u32,
@@ -118,7 +118,7 @@
         _value: &T,
     ) -> Result<InternalString, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         Err(Error::KeyNotString)
     }
diff --git a/crates/toml_edit/src/ser/map.rs b/crates/toml_edit/src/ser/map.rs
index 47e56ba..4902902 100644
--- a/crates/toml_edit/src/ser/map.rs
+++ b/crates/toml_edit/src/ser/map.rs
@@ -24,9 +24,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_key<T: ?Sized>(&mut self, input: &T) -> Result<(), Self::Error>
+    fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         match self {
             Self::Datetime(s) => s.serialize_key(input),
@@ -34,9 +34,9 @@
         }
     }
 
-    fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+    fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         match self {
             Self::Datetime(s) => s.serialize_value(value),
@@ -56,13 +56,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_field<T: ?Sized>(
-        &mut self,
-        key: &'static str,
-        value: &T,
-    ) -> Result<(), Self::Error>
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         match self {
             Self::Datetime(s) => s.serialize_field(key, value),
@@ -93,16 +89,16 @@
     type Ok = crate::Datetime;
     type Error = Error;
 
-    fn serialize_key<T: ?Sized>(&mut self, _input: &T) -> Result<(), Self::Error>
+    fn serialize_key<T>(&mut self, _input: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         unreachable!("datetimes should only be serialized as structs, not maps")
     }
 
-    fn serialize_value<T: ?Sized>(&mut self, _value: &T) -> Result<(), Self::Error>
+    fn serialize_value<T>(&mut self, _value: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         unreachable!("datetimes should only be serialized as structs, not maps")
     }
@@ -116,13 +112,9 @@
     type Ok = crate::Datetime;
     type Error = Error;
 
-    fn serialize_field<T: ?Sized>(
-        &mut self,
-        key: &'static str,
-        value: &T,
-    ) -> Result<(), Self::Error>
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         if key == toml_datetime::__unstable::FIELD {
             self.value = Some(value.serialize(DatetimeFieldSerializer::default())?);
@@ -161,17 +153,17 @@
     type Ok = crate::InlineTable;
     type Error = Error;
 
-    fn serialize_key<T: ?Sized>(&mut self, input: &T) -> Result<(), Self::Error>
+    fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         self.key = Some(input.serialize(KeySerializer)?);
         Ok(())
     }
 
-    fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+    fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         let mut value_serializer = MapValueSerializer::new();
         let res = value.serialize(&mut value_serializer);
@@ -202,13 +194,9 @@
     type Ok = crate::InlineTable;
     type Error = Error;
 
-    fn serialize_field<T: ?Sized>(
-        &mut self,
-        key: &'static str,
-        value: &T,
-    ) -> Result<(), Self::Error>
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         let mut value_serializer = MapValueSerializer::new();
         let res = value.serialize(&mut value_serializer);
@@ -308,9 +296,9 @@
         Err(Error::DateInvalid)
     }
 
-    fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
+    fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         Err(Error::DateInvalid)
     }
@@ -332,18 +320,18 @@
         Err(Error::DateInvalid)
     }
 
-    fn serialize_newtype_struct<T: ?Sized>(
+    fn serialize_newtype_struct<T>(
         self,
         _name: &'static str,
         _value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         Err(Error::DateInvalid)
     }
 
-    fn serialize_newtype_variant<T: ?Sized>(
+    fn serialize_newtype_variant<T>(
         self,
         _name: &'static str,
         _variant_index: u32,
@@ -351,7 +339,7 @@
         _value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         Err(Error::DateInvalid)
     }
@@ -419,13 +407,13 @@
 impl serde::ser::Serializer for &mut MapValueSerializer {
     type Ok = crate::Value;
     type Error = Error;
-    type SerializeSeq = super::SerializeValueArray;
-    type SerializeTuple = super::SerializeValueArray;
-    type SerializeTupleStruct = super::SerializeValueArray;
-    type SerializeTupleVariant = super::SerializeTupleVariant;
-    type SerializeMap = super::SerializeMap;
-    type SerializeStruct = super::SerializeMap;
-    type SerializeStructVariant = super::SerializeStructVariant;
+    type SerializeSeq = SerializeValueArray;
+    type SerializeTuple = SerializeValueArray;
+    type SerializeTupleStruct = SerializeValueArray;
+    type SerializeTupleVariant = SerializeTupleVariant;
+    type SerializeMap = SerializeMap;
+    type SerializeStruct = SerializeMap;
+    type SerializeStructVariant = SerializeStructVariant;
 
     fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
         ValueSerializer::new().serialize_bool(v)
@@ -488,9 +476,9 @@
         Err(Error::UnsupportedNone)
     }
 
-    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
+    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         ValueSerializer::new().serialize_some(value)
     }
@@ -512,18 +500,18 @@
         ValueSerializer::new().serialize_unit_variant(name, variant_index, variant)
     }
 
-    fn serialize_newtype_struct<T: ?Sized>(
+    fn serialize_newtype_struct<T>(
         self,
         name: &'static str,
         value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         ValueSerializer::new().serialize_newtype_struct(name, value)
     }
 
-    fn serialize_newtype_variant<T: ?Sized>(
+    fn serialize_newtype_variant<T>(
         self,
         name: &'static str,
         variant_index: u32,
@@ -531,7 +519,7 @@
         value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         ValueSerializer::new().serialize_newtype_variant(name, variant_index, variant, value)
     }
@@ -585,8 +573,8 @@
     }
 }
 
-pub type SerializeTupleVariant = SerializeVariant<SerializeValueArray>;
-pub type SerializeStructVariant = SerializeVariant<SerializeMap>;
+pub(crate) type SerializeTupleVariant = SerializeVariant<SerializeValueArray>;
+pub(crate) type SerializeStructVariant = SerializeVariant<SerializeMap>;
 
 pub struct SerializeVariant<T> {
     variant: &'static str,
@@ -615,9 +603,9 @@
     type Ok = crate::Value;
     type Error = Error;
 
-    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         serde::ser::SerializeSeq::serialize_element(&mut self.inner, value)
     }
diff --git a/crates/toml_edit/src/ser/mod.rs b/crates/toml_edit/src/ser/mod.rs
index ba31708..3cf00c3 100644
--- a/crates/toml_edit/src/ser/mod.rs
+++ b/crates/toml_edit/src/ser/mod.rs
@@ -51,7 +51,7 @@
 }
 
 impl std::fmt::Display for Error {
-    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             Self::UnsupportedType(Some(t)) => write!(formatter, "unsupported {t} type"),
             Self::UnsupportedType(None) => write!(formatter, "unsupported rust type"),
@@ -85,9 +85,9 @@
 /// fail, if `T` contains a map with non-string keys, or if `T` attempts to
 /// serialize an unsupported datatype such as an enum, tuple, or tuple struct.
 #[cfg(feature = "display")]
-pub fn to_vec<T: ?Sized>(value: &T) -> Result<Vec<u8>, Error>
+pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, Error>
 where
-    T: serde::ser::Serialize,
+    T: serde::ser::Serialize + ?Sized,
 {
     to_string(value).map(|e| e.into_bytes())
 }
@@ -129,9 +129,9 @@
 /// println!("{}", toml)
 /// ```
 #[cfg(feature = "display")]
-pub fn to_string<T: ?Sized>(value: &T) -> Result<String, Error>
+pub fn to_string<T>(value: &T) -> Result<String, Error>
 where
-    T: serde::ser::Serialize,
+    T: serde::ser::Serialize + ?Sized,
 {
     to_document(value).map(|e| e.to_string())
 }
@@ -141,9 +141,9 @@
 /// This is identical to `to_string` except the output string has a more
 /// "pretty" output. See `ValueSerializer::pretty` for more details.
 #[cfg(feature = "display")]
-pub fn to_string_pretty<T: ?Sized>(value: &T) -> Result<String, Error>
+pub fn to_string_pretty<T>(value: &T) -> Result<String, Error>
 where
-    T: serde::ser::Serialize,
+    T: serde::ser::Serialize + ?Sized,
 {
     let mut document = to_document(value)?;
     pretty::Pretty.visit_document_mut(&mut document);
@@ -153,9 +153,9 @@
 /// Serialize the given data structure into a TOML document.
 ///
 /// This would allow custom formatting to be applied, mixing with format preserving edits, etc.
-pub fn to_document<T: ?Sized>(value: &T) -> Result<crate::Document, Error>
+pub fn to_document<T>(value: &T) -> Result<crate::DocumentMut, Error>
 where
-    T: serde::ser::Serialize,
+    T: serde::ser::Serialize + ?Sized,
 {
     let value = value.serialize(ValueSerializer::new())?;
     let item = crate::Item::Value(value);
diff --git a/crates/toml_edit/src/ser/pretty.rs b/crates/toml_edit/src/ser/pretty.rs
index 2c22f68..05552bf 100644
--- a/crates/toml_edit/src/ser/pretty.rs
+++ b/crates/toml_edit/src/ser/pretty.rs
@@ -1,7 +1,7 @@
 pub(crate) struct Pretty;
 
 impl crate::visit_mut::VisitMut for Pretty {
-    fn visit_document_mut(&mut self, node: &mut crate::Document) {
+    fn visit_document_mut(&mut self, node: &mut crate::DocumentMut) {
         crate::visit_mut::visit_document_mut(self, node);
     }
 
diff --git a/crates/toml_edit/src/ser/value.rs b/crates/toml_edit/src/ser/value.rs
index 47a17a3..b67bd8a 100644
--- a/crates/toml_edit/src/ser/value.rs
+++ b/crates/toml_edit/src/ser/value.rs
@@ -13,6 +13,8 @@
 /// # Examples
 ///
 /// ```
+/// # #[cfg(feature = "parse")] {
+/// # #[cfg(feature = "display")] {
 /// use serde::Serialize;
 ///
 /// #[derive(Serialize)]
@@ -42,6 +44,8 @@
 ///     toml_edit::ser::ValueSerializer::new()
 /// ).unwrap();
 /// println!("{}", value)
+/// # }
+/// # }
 /// ```
 #[derive(Default)]
 #[non_exhaustive]
@@ -140,9 +144,9 @@
         Err(Error::UnsupportedNone)
     }
 
-    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
+    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         value.serialize(self)
     }
@@ -164,18 +168,18 @@
         self.serialize_str(variant)
     }
 
-    fn serialize_newtype_struct<T: ?Sized>(
+    fn serialize_newtype_struct<T>(
         self,
         _name: &'static str,
         value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         value.serialize(self)
     }
 
-    fn serialize_newtype_variant<T: ?Sized>(
+    fn serialize_newtype_variant<T>(
         self,
         _name: &'static str,
         _variant_index: u32,
@@ -183,7 +187,7 @@
         value: &T,
     ) -> Result<Self::Ok, Self::Error>
     where
-        T: serde::ser::Serialize,
+        T: serde::ser::Serialize + ?Sized,
     {
         let value = value.serialize(self)?;
         let mut table = crate::InlineTable::new();
diff --git a/crates/toml_edit/src/table.rs b/crates/toml_edit/src/table.rs
index cc0d2b2..2863946 100644
--- a/crates/toml_edit/src/table.rs
+++ b/crates/toml_edit/src/table.rs
@@ -167,8 +167,8 @@
     /// ```
     /// # #[cfg(feature = "parse")] {
     /// # #[cfg(feature = "display")] {
-    /// use toml_edit::Document;
-    /// let mut doc = "[a]\n[a.b]\n".parse::<Document>().expect("invalid toml");
+    /// use toml_edit::DocumentMut;
+    /// let mut doc = "[a]\n[a.b]\n".parse::<DocumentMut>().expect("invalid toml");
     ///
     /// doc["a"].as_table_mut().unwrap().set_implicit(true);
     /// assert_eq!(doc.to_string(), "[a.b]\n");
@@ -194,12 +194,12 @@
         self.dotted
     }
 
-    /// Sets the position of the `Table` within the `Document`.
+    /// Sets the position of the `Table` within the [`DocumentMut`][crate::DocumentMut].
     pub fn set_position(&mut self, doc_position: usize) {
         self.doc_position = Some(doc_position);
     }
 
-    /// The position of the `Table` within the `Document`.
+    /// The position of the `Table` within the [`DocumentMut`][crate::DocumentMut].
     ///
     /// Returns `None` if the `Table` was created manually (i.e. not via parsing)
     /// in which case its position is set automatically.  This can be overridden with
@@ -242,8 +242,10 @@
         self.items.get(key).map(|kv| kv.key.leaf_decor())
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         self.span.clone()
     }
 
@@ -290,7 +292,7 @@
 
     /// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
     pub fn clear(&mut self) {
-        self.items.clear()
+        self.items.clear();
     }
 
     /// Gets the given key's corresponding entry in the Table for in-place manipulation.
@@ -502,6 +504,7 @@
 }
 
 // `key1 = value1`
+pub(crate) const DEFAULT_ROOT_DECOR: (&str, &str) = ("", "");
 pub(crate) const DEFAULT_KEY_DECOR: (&str, &str) = ("", " ");
 pub(crate) const DEFAULT_TABLE_DECOR: (&str, &str) = ("\n", "");
 pub(crate) const DEFAULT_KEY_PATH_DECOR: (&str, &str) = ("", "");
@@ -630,16 +633,16 @@
         self.get_values()
     }
     fn fmt(&mut self) {
-        self.fmt()
+        self.fmt();
     }
     fn sort_values(&mut self) {
-        self.sort_values()
+        self.sort_values();
     }
     fn is_dotted(&self) -> bool {
         self.is_dotted()
     }
     fn set_dotted(&mut self, yes: bool) {
-        self.set_dotted(yes)
+        self.set_dotted(yes);
     }
 
     fn key(&self, key: &str) -> Option<&'_ Key> {
@@ -740,7 +743,7 @@
         &mut self.entry.get_mut().value
     }
 
-    /// Converts the OccupiedEntry into a mutable reference to the value in the entry
+    /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
     /// with a lifetime bound to the map itself
     pub fn into_mut(self) -> &'a mut Item {
         &mut self.entry.into_mut().value
@@ -780,7 +783,7 @@
         self.entry.key().as_str()
     }
 
-    /// Sets the value of the entry with the VacantEntry's key,
+    /// Sets the value of the entry with the `VacantEntry`'s key,
     /// and returns a mutable reference to it
     pub fn insert(self, value: Item) -> &'a mut Item {
         let entry = self.entry;
diff --git a/crates/toml_edit/src/value.rs b/crates/toml_edit/src/value.rs
index f416434..02ee5e3 100644
--- a/crates/toml_edit/src/value.rs
+++ b/crates/toml_edit/src/value.rs
@@ -1,7 +1,7 @@
 use std::iter::FromIterator;
 use std::str::FromStr;
 
-use toml_datetime::*;
+use toml_datetime::{Date, Datetime, Time};
 
 use crate::key::Key;
 use crate::repr::{Decor, Formatted};
@@ -49,7 +49,7 @@
         }
     }
 
-    /// Returns true iff `self` is a string.
+    /// Returns true if `self` is a string.
     pub fn is_str(&self) -> bool {
         self.as_str().is_some()
     }
@@ -62,7 +62,7 @@
         }
     }
 
-    /// Returns true iff `self` is an integer.
+    /// Returns true if `self` is an integer.
     pub fn is_integer(&self) -> bool {
         self.as_integer().is_some()
     }
@@ -75,7 +75,7 @@
         }
     }
 
-    /// Returns true iff `self` is a float.
+    /// Returns true if `self` is a float.
     pub fn is_float(&self) -> bool {
         self.as_float().is_some()
     }
@@ -88,7 +88,7 @@
         }
     }
 
-    /// Returns true iff `self` is a boolean.
+    /// Returns true if `self` is a boolean.
     pub fn is_bool(&self) -> bool {
         self.as_bool().is_some()
     }
@@ -101,7 +101,7 @@
         }
     }
 
-    /// Returns true iff `self` is a date-time.
+    /// Returns true if `self` is a date-time.
     pub fn is_datetime(&self) -> bool {
         self.as_datetime().is_some()
     }
@@ -122,7 +122,7 @@
         }
     }
 
-    /// Returns true iff `self` is an array.
+    /// Returns true if `self` is an array.
     pub fn is_array(&self) -> bool {
         self.as_array().is_some()
     }
@@ -143,7 +143,7 @@
         }
     }
 
-    /// Returns true iff `self` is an inline table.
+    /// Returns true if `self` is an inline table.
     pub fn is_inline_table(&self) -> bool {
         self.as_inline_table().is_some()
     }
@@ -206,8 +206,10 @@
         *decor = Decor::new(prefix, suffix);
     }
 
-    /// Returns the location within the original document
-    pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
+    /// The location within the original document
+    ///
+    /// This generally requires an [`ImDocument`][crate::ImDocument].
+    pub fn span(&self) -> Option<std::ops::Range<usize>> {
         match self {
             Value::String(f) => f.span(),
             Value::Integer(f) => f.span(),
@@ -371,8 +373,15 @@
 
     #[test]
     fn from_iter_formatting() {
-        let features = vec!["node".to_owned(), "mouth".to_owned()];
+        let features = ["node".to_owned(), "mouth".to_owned()];
         let features: Value = features.iter().cloned().collect();
         assert_eq!(features.to_string(), r#"["node", "mouth"]"#);
     }
 }
+
+#[test]
+#[cfg(feature = "parse")]
+#[cfg(feature = "display")]
+fn string_roundtrip() {
+    Value::from("hello").to_string().parse::<Value>().unwrap();
+}
diff --git a/crates/toml_edit/src/visit.rs b/crates/toml_edit/src/visit.rs
index 6d7be0b..ee8c9f9 100644
--- a/crates/toml_edit/src/visit.rs
+++ b/crates/toml_edit/src/visit.rs
@@ -63,7 +63,7 @@
 //! the-force = { value = "surrounds-you" }
 //! "#;
 //!
-//! let mut document: Document = input.parse().unwrap();
+//! let mut document: DocumentMut = input.parse().unwrap();
 //! let mut visitor = StringCollector::default();
 //! visitor.visit_document(&document);
 //!
@@ -75,14 +75,15 @@
 //! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
 
 use crate::{
-    Array, ArrayOfTables, Datetime, Document, Formatted, InlineTable, Item, Table, TableLike, Value,
+    Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, Table, TableLike,
+    Value,
 };
 
 /// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
 ///
 /// See the [module documentation](self) for details.
 pub trait Visit<'doc> {
-    fn visit_document(&mut self, node: &'doc Document) {
+    fn visit_document(&mut self, node: &'doc DocumentMut) {
         visit_document(self, node);
     }
 
@@ -95,7 +96,7 @@
     }
 
     fn visit_inline_table(&mut self, node: &'doc InlineTable) {
-        visit_inline_table(self, node)
+        visit_inline_table(self, node);
     }
 
     fn visit_table_like(&mut self, node: &'doc dyn TableLike) {
@@ -119,7 +120,7 @@
     }
 
     fn visit_boolean(&mut self, node: &'doc Formatted<bool>) {
-        visit_boolean(self, node)
+        visit_boolean(self, node);
     }
 
     fn visit_datetime(&mut self, node: &'doc Formatted<Datetime>) {
@@ -127,19 +128,19 @@
     }
 
     fn visit_float(&mut self, node: &'doc Formatted<f64>) {
-        visit_float(self, node)
+        visit_float(self, node);
     }
 
     fn visit_integer(&mut self, node: &'doc Formatted<i64>) {
-        visit_integer(self, node)
+        visit_integer(self, node);
     }
 
     fn visit_string(&mut self, node: &'doc Formatted<String>) {
-        visit_string(self, node)
+        visit_string(self, node);
     }
 }
 
-pub fn visit_document<'doc, V>(v: &mut V, node: &'doc Document)
+pub fn visit_document<'doc, V>(v: &mut V, node: &'doc DocumentMut)
 where
     V: Visit<'doc> + ?Sized,
 {
@@ -162,14 +163,14 @@
 where
     V: Visit<'doc> + ?Sized,
 {
-    v.visit_table_like(node)
+    v.visit_table_like(node);
 }
 
 pub fn visit_inline_table<'doc, V>(v: &mut V, node: &'doc InlineTable)
 where
     V: Visit<'doc> + ?Sized,
 {
-    v.visit_table_like(node)
+    v.visit_table_like(node);
 }
 
 pub fn visit_table_like<'doc, V>(v: &mut V, node: &'doc dyn TableLike)
@@ -177,7 +178,7 @@
     V: Visit<'doc> + ?Sized,
 {
     for (key, item) in node.iter() {
-        v.visit_table_like_kv(key, item)
+        v.visit_table_like_kv(key, item);
     }
 }
 
@@ -185,7 +186,7 @@
 where
     V: Visit<'doc> + ?Sized,
 {
-    v.visit_item(node)
+    v.visit_item(node);
 }
 
 pub fn visit_array<'doc, V>(v: &mut V, node: &'doc Array)
diff --git a/crates/toml_edit/src/visit_mut.rs b/crates/toml_edit/src/visit_mut.rs
index c823cfb..ebaf1e5 100644
--- a/crates/toml_edit/src/visit_mut.rs
+++ b/crates/toml_edit/src/visit_mut.rs
@@ -72,7 +72,7 @@
 //! table = { apple = 4.5 }
 //! "#;
 //!
-//! let mut document: Document = input.parse().unwrap();
+//! let mut document: DocumentMut = input.parse().unwrap();
 //! let mut visitor = FloatToString;
 //! visitor.visit_document_mut(&mut document);
 //!
@@ -90,7 +90,7 @@
 //! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
 
 use crate::{
-    Array, ArrayOfTables, Datetime, Document, Formatted, InlineTable, Item, KeyMut, Table,
+    Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, KeyMut, Table,
     TableLike, Value,
 };
 
@@ -98,7 +98,7 @@
 ///
 /// See the [module documentation](self) for details.
 pub trait VisitMut {
-    fn visit_document_mut(&mut self, node: &mut Document) {
+    fn visit_document_mut(&mut self, node: &mut DocumentMut) {
         visit_document_mut(self, node);
     }
 
@@ -111,7 +111,7 @@
     }
 
     fn visit_inline_table_mut(&mut self, node: &mut InlineTable) {
-        visit_inline_table_mut(self, node)
+        visit_inline_table_mut(self, node);
     }
 
     /// [`visit_table_mut`](Self::visit_table_mut) and
@@ -137,7 +137,7 @@
     }
 
     fn visit_boolean_mut(&mut self, node: &mut Formatted<bool>) {
-        visit_boolean_mut(self, node)
+        visit_boolean_mut(self, node);
     }
 
     fn visit_datetime_mut(&mut self, node: &mut Formatted<Datetime>) {
@@ -145,19 +145,19 @@
     }
 
     fn visit_float_mut(&mut self, node: &mut Formatted<f64>) {
-        visit_float_mut(self, node)
+        visit_float_mut(self, node);
     }
 
     fn visit_integer_mut(&mut self, node: &mut Formatted<i64>) {
-        visit_integer_mut(self, node)
+        visit_integer_mut(self, node);
     }
 
     fn visit_string_mut(&mut self, node: &mut Formatted<String>) {
-        visit_string_mut(self, node)
+        visit_string_mut(self, node);
     }
 }
 
-pub fn visit_document_mut<V>(v: &mut V, node: &mut Document)
+pub fn visit_document_mut<V>(v: &mut V, node: &mut DocumentMut)
 where
     V: VisitMut + ?Sized,
 {
@@ -203,7 +203,7 @@
 where
     V: VisitMut + ?Sized,
 {
-    v.visit_item_mut(node)
+    v.visit_item_mut(node);
 }
 
 pub fn visit_array_mut<V>(v: &mut V, node: &mut Array)
diff --git a/crates/toml_edit/tests/decoder.rs b/crates/toml_edit/tests/decoder.rs
index 7306d45..f8cf02c 100644
--- a/crates/toml_edit/tests/decoder.rs
+++ b/crates/toml_edit/tests/decoder.rs
@@ -1,5 +1,5 @@
 #[derive(Copy, Clone)]
-pub struct Decoder;
+pub(crate) struct Decoder;
 
 impl toml_test_harness::Decoder for Decoder {
     fn name(&self) -> &str {
@@ -9,14 +9,14 @@
     fn decode(&self, data: &[u8]) -> Result<toml_test_harness::Decoded, toml_test_harness::Error> {
         let data = std::str::from_utf8(data).map_err(toml_test_harness::Error::new)?;
         let document = data
-            .parse::<toml_edit::Document>()
+            .parse::<toml_edit::DocumentMut>()
             .map_err(toml_test_harness::Error::new)?;
         document_to_decoded(&document)
     }
 }
 
 fn document_to_decoded(
-    value: &toml_edit::Document,
+    value: &toml_edit::DocumentMut,
 ) -> Result<toml_test_harness::Decoded, toml_test_harness::Error> {
     table_to_decoded(value)
 }
diff --git a/crates/toml_edit/tests/encoder.rs b/crates/toml_edit/tests/encoder.rs
index 808a20c..e325aca 100644
--- a/crates/toml_edit/tests/encoder.rs
+++ b/crates/toml_edit/tests/encoder.rs
@@ -1,5 +1,5 @@
 #[derive(Copy, Clone)]
-pub struct Encoder;
+pub(crate) struct Encoder;
 
 impl toml_test_harness::Encoder for Encoder {
     fn name(&self) -> &str {
@@ -14,9 +14,9 @@
 
 fn decoded_to_document(
     decoded: &toml_test_harness::Decoded,
-) -> Result<toml_edit::Document, toml_test_harness::Error> {
+) -> Result<toml_edit::DocumentMut, toml_test_harness::Error> {
     let item = root_from_decoded(decoded)?;
-    let mut doc = toml_edit::Document::new();
+    let mut doc = toml_edit::DocumentMut::new();
     *doc = item;
     Ok(doc)
 }
diff --git a/crates/toml_edit/tests/fixtures/invalid/datetime/offset-overflow-hour.stderr b/crates/toml_edit/tests/fixtures/invalid/datetime/offset-overflow-hour.stderr
new file mode 100644
index 0000000..f2254c4
--- /dev/null
+++ b/crates/toml_edit/tests/fixtures/invalid/datetime/offset-overflow-hour.stderr
@@ -0,0 +1,6 @@
+TOML parse error at line 2, column 25
+  |
+2 | d = 1985-06-18 17:04:07+25:00
+  |                         ^
+invalid time offset
+value is out of range
diff --git a/crates/toml_edit/tests/fixtures/invalid/datetime/offset-overflow-minute.stderr b/crates/toml_edit/tests/fixtures/invalid/datetime/offset-overflow-minute.stderr
new file mode 100644
index 0000000..ce42d2e
--- /dev/null
+++ b/crates/toml_edit/tests/fixtures/invalid/datetime/offset-overflow-minute.stderr
@@ -0,0 +1,6 @@
+TOML parse error at line 3, column 28
+  |
+3 | d = 1985-06-18 17:04:07+12:61
+  |                            ^
+invalid time offset
+value is out of range
diff --git a/crates/toml_edit/tests/fixtures/invalid/key/special-character.stderr b/crates/toml_edit/tests/fixtures/invalid/key/special-character.stderr
index 7ada2f2..10592cf 100644
--- a/crates/toml_edit/tests/fixtures/invalid/key/special-character.stderr
+++ b/crates/toml_edit/tests/fixtures/invalid/key/special-character.stderr
@@ -1,5 +1,5 @@
 TOML parse error at line 1, column 1
   |
 1 | μ = "greek small letter mu"
-  | ^
+  | ^^
 invalid key
diff --git a/crates/toml_edit/tests/fixtures/invalid/table/no-close-2.stderr b/crates/toml_edit/tests/fixtures/invalid/table/no-close-2.stderr
index a7adf38..26fc787 100644
--- a/crates/toml_edit/tests/fixtures/invalid/table/no-close-2.stderr
+++ b/crates/toml_edit/tests/fixtures/invalid/table/no-close-2.stderr
@@ -1,6 +1,6 @@
 TOML parse error at line 1, column 25
   |
 1 | [closing-bracket.missingö
-  |                         ^
+  |                         ^^
 invalid table header
 expected `.`, `]`
diff --git a/crates/toml_edit/tests/invalid.rs b/crates/toml_edit/tests/invalid.rs
index 9fff235..9bce553 100644
--- a/crates/toml_edit/tests/invalid.rs
+++ b/crates/toml_edit/tests/invalid.rs
@@ -1,4 +1,4 @@
-use toml_edit::Document;
+use toml_edit::DocumentMut;
 
 fn main() {
     let args = libtest_mimic::Arguments::from_args();
@@ -11,7 +11,7 @@
                     Ok(()) => "".to_owned(),
                     Err(err) => err,
                 };
-                snapbox::assert_eq_path(expect_path, err);
+                snapbox::assert_data_eq!(err, snapbox::Data::read_from(&expect_path, None).raw());
                 Ok(())
             })
         })
@@ -21,6 +21,6 @@
 
 fn run_case(input: &[u8]) -> Result<(), String> {
     let raw = std::str::from_utf8(input).map_err(|e| e.to_string())?;
-    let _ = raw.parse::<Document>().map_err(|e| e.to_string())?;
+    let _ = raw.parse::<DocumentMut>().map_err(|e| e.to_string())?;
     Ok(())
 }
diff --git a/crates/toml_edit/tests/testsuite/convert.rs b/crates/toml_edit/tests/testsuite/convert.rs
index 98f9397..4e542a2 100644
--- a/crates/toml_edit/tests/testsuite/convert.rs
+++ b/crates/toml_edit/tests/testsuite/convert.rs
@@ -1,6 +1,8 @@
-use snapbox::assert_eq;
+use snapbox::assert_data_eq;
+use snapbox::prelude::*;
+use snapbox::str;
 
-use toml_edit::{Document, Item, Value};
+use toml_edit::{DocumentMut, Item, Value};
 
 #[test]
 fn table_into_inline() {
@@ -13,22 +15,23 @@
 [table.child]
 other = "world"
 "#;
-    let mut doc = toml.parse::<Document>().unwrap();
+    let mut doc = toml.parse::<DocumentMut>().unwrap();
 
     doc.get_mut("table").unwrap().make_value();
 
     let actual = doc.to_string();
     // `table=` is because we didn't re-format the table key, only the value
-    let expected = r#"table= { string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }
-"#;
-    assert_eq(expected, actual);
+    assert_data_eq!(actual, str![[r#"
+table= { string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }
+
+"#]].raw());
 }
 
 #[test]
 fn inline_table_to_table() {
     let toml = r#"table = { string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }
 "#;
-    let mut doc = toml.parse::<Document>().unwrap();
+    let mut doc = toml.parse::<DocumentMut>().unwrap();
 
     let t = doc.remove("table").unwrap();
     let t = match t {
@@ -39,13 +42,18 @@
     doc.insert("table", Item::Table(t));
 
     let actual = doc.to_string();
-    let expected = r#"[table]
+    assert_data_eq!(
+        actual,
+        str![[r#"
+[table]
 string = "value"
 array = [1, 2, 3]
 inline = { "1" = 1, "2" = 2 }
 child = { other = "world" }
-"#;
-    assert_eq(expected, actual);
+
+"#]]
+        .raw()
+    );
 }
 
 #[test]
@@ -67,13 +75,14 @@
 [table.child]
 other = "world"
 "#;
-    let mut doc = toml.parse::<Document>().unwrap();
+    let mut doc = toml.parse::<DocumentMut>().unwrap();
 
     doc.get_mut("table").unwrap().make_value();
 
     let actual = doc.to_string();
     // `table=` is because we didn't re-format the table key, only the value
-    let expected = r#"table= [{ string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }, { string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }]
-"#;
-    assert_eq(expected, actual);
+    assert_data_eq!(actual, str![[r#"
+table= [{ string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }, { string = "value", array = [1, 2, 3], inline = { "1" = 1, "2" = 2 }, child = { other = "world" } }]
+
+"#]].raw());
 }
diff --git a/crates/toml_edit/tests/testsuite/datetime.rs b/crates/toml_edit/tests/testsuite/datetime.rs
index 541f8ea..dba89ec 100644
--- a/crates/toml_edit/tests/testsuite/datetime.rs
+++ b/crates/toml_edit/tests/testsuite/datetime.rs
@@ -1,8 +1,12 @@
+use snapbox::assert_data_eq;
+use snapbox::prelude::*;
+use snapbox::str;
+
 macro_rules! bad {
     ($toml:expr, $msg:expr) => {
-        match $toml.parse::<toml_edit::Document>() {
+        match $toml.parse::<toml_edit::DocumentMut>() {
             Ok(s) => panic!("parsed to: {:#?}", s),
-            Err(e) => snapbox::assert_eq($msg, e.to_string()),
+            Err(e) => assert_data_eq!(e.to_string(), $msg.raw()),
         }
     };
 }
@@ -11,7 +15,7 @@
 fn times() {
     fn dogood(s: &str, serialized: &str) {
         let to_parse = format!("foo = {}", s);
-        let document = to_parse.parse::<toml_edit::Document>().unwrap();
+        let document = to_parse.parse::<toml_edit::DocumentMut>().unwrap();
         assert_eq!(
             document["foo"].as_datetime().unwrap().to_string(),
             serialized
@@ -43,214 +47,252 @@
 fn bad_times() {
     bad!(
         "foo = 199-09-09",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
 1 | foo = 199-09-09
   |          ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = 199709-09",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 13
   |
 1 | foo = 199709-09
   |             ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = 1997-9-09",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 12
   |
 1 | foo = 1997-9-09
   |            ^
 invalid date-time
-"
+
+"#]]
     );
     bad!(
         "foo = 1997-09-9",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 15
   |
 1 | foo = 1997-09-9
   |               ^
 invalid date-time
-"
+
+"#]]
     );
     bad!(
         "foo = 1997-09-0909:09:09",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 17
   |
 1 | foo = 1997-09-0909:09:09
   |                 ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 26
   |
 1 | foo = 1997-09-09T09:09:09.
   |                          ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = T",
-        r#"TOML parse error at line 1, column 7
+        str![[r#"
+TOML parse error at line 1, column 7
   |
 1 | foo = T
   |       ^
 invalid string
 expected `"`, `'`
-"#
+
+"#]]
     );
     bad!(
         "foo = T.",
-        r#"TOML parse error at line 1, column 7
+        str![[r#"
+TOML parse error at line 1, column 7
   |
 1 | foo = T.
   |       ^
 invalid string
 expected `"`, `'`
-"#
+
+"#]]
     );
     bad!(
         "foo = TZ",
-        r#"TOML parse error at line 1, column 7
+        str![[r#"
+TOML parse error at line 1, column 7
   |
 1 | foo = TZ
   |       ^
 invalid string
 expected `"`, `'`
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09+",
-        r#"TOML parse error at line 1, column 30
+        str![[r#"
+TOML parse error at line 1, column 30
   |
 1 | foo = 1997-09-09T09:09:09.09+
   |                              ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09+09",
-        r#"TOML parse error at line 1, column 32
+        str![[r#"
+TOML parse error at line 1, column 32
   |
 1 | foo = 1997-09-09T09:09:09.09+09
   |                                ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09+09:9",
-        r#"TOML parse error at line 1, column 33
+        str![[r#"
+TOML parse error at line 1, column 33
   |
 1 | foo = 1997-09-09T09:09:09.09+09:9
   |                                 ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09+0909",
-        r#"TOML parse error at line 1, column 32
+        str![[r#"
+TOML parse error at line 1, column 32
   |
 1 | foo = 1997-09-09T09:09:09.09+0909
   |                                ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09-",
-        r#"TOML parse error at line 1, column 30
+        str![[r#"
+TOML parse error at line 1, column 30
   |
 1 | foo = 1997-09-09T09:09:09.09-
   |                              ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09-09",
-        r#"TOML parse error at line 1, column 32
+        str![[r#"
+TOML parse error at line 1, column 32
   |
 1 | foo = 1997-09-09T09:09:09.09-09
   |                                ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09-09:9",
-        r#"TOML parse error at line 1, column 33
+        str![[r#"
+TOML parse error at line 1, column 33
   |
 1 | foo = 1997-09-09T09:09:09.09-09:9
   |                                 ^
 invalid time offset
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T09:09:09.09-0909",
-        r#"TOML parse error at line 1, column 32
+        str![[r#"
+TOML parse error at line 1, column 32
   |
 1 | foo = 1997-09-09T09:09:09.09-0909
   |                                ^
 invalid time offset
-"#
+
+"#]]
     );
 
     bad!(
         "foo = 1997-00-09T09:09:09.09Z",
-        r#"TOML parse error at line 1, column 12
+        str![[r#"
+TOML parse error at line 1, column 12
   |
 1 | foo = 1997-00-09T09:09:09.09Z
   |            ^
 invalid date-time
 value is out of range
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-00T09:09:09.09Z",
-        r#"TOML parse error at line 1, column 15
+        str![[r#"
+TOML parse error at line 1, column 15
   |
 1 | foo = 1997-09-00T09:09:09.09Z
   |               ^
 invalid date-time
 value is out of range
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T30:09:09.09Z",
-        r#"TOML parse error at line 1, column 17
+        str![[r#"
+TOML parse error at line 1, column 17
   |
 1 | foo = 1997-09-09T30:09:09.09Z
   |                 ^
 expected newline, `#`
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T12:69:09.09Z",
-        r#"TOML parse error at line 1, column 21
+        str![[r#"
+TOML parse error at line 1, column 21
   |
 1 | foo = 1997-09-09T12:69:09.09Z
   |                     ^
 invalid date-time
 value is out of range
-"#
+
+"#]]
     );
     bad!(
         "foo = 1997-09-09T12:09:69.09Z",
-        r#"TOML parse error at line 1, column 24
+        str![[r#"
+TOML parse error at line 1, column 24
   |
 1 | foo = 1997-09-09T12:09:69.09Z
   |                        ^
 invalid date-time
 value is out of range
-"#
+
+"#]]
     );
 }
diff --git a/crates/toml_edit/tests/testsuite/edit.rs b/crates/toml_edit/tests/testsuite/edit.rs
index 94c20d4..01d9ef0 100644
--- a/crates/toml_edit/tests/testsuite/edit.rs
+++ b/crates/toml_edit/tests/testsuite/edit.rs
@@ -1,8 +1,9 @@
-use std::fmt;
 use std::iter::FromIterator;
 
-use snapbox::assert_eq;
-use toml_edit::{array, table, value, Document, Item, Key, Table, Value};
+use snapbox::assert_data_eq;
+use snapbox::prelude::*;
+use snapbox::str;
+use toml_edit::{array, table, value, DocumentMut, Item, Key, Table, Value};
 
 macro_rules! parse_key {
     ($s:expr) => {{
@@ -19,25 +20,12 @@
     }};
 }
 
-// Copied from https://github.com/colin-kiegel/rust-pretty-assertions/issues/24
-/// Wrapper around string slice that makes debug output `{:?}` to print string same way as `{}`.
-/// Used in different `assert*!` macros in combination with `pretty_assertions` crate to make
-/// test failures to show nice diffs.
-#[derive(PartialEq, Eq)]
-struct PrettyString<'a>(pub &'a str);
-/// Make diff to display string as multi-line string
-impl<'a> fmt::Debug for PrettyString<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(self.0)
-    }
-}
-
 struct Test {
-    doc: Document,
+    doc: DocumentMut,
 }
 
 fn given(input: &str) -> Test {
-    let doc = input.parse::<Document>();
+    let doc = input.parse::<DocumentMut>();
     assert!(doc.is_ok());
     Test { doc: doc.unwrap() }
 }
@@ -53,12 +41,112 @@
         }
         self
     }
-
-    #[track_caller]
-    fn produces_display(&self, expected: &str) -> &Self {
-        assert_eq(expected, self.doc.to_string());
+    fn running_on_doc<F>(&mut self, func: F) -> &mut Self
+    where
+        F: Fn(&mut DocumentMut),
+    {
+        {
+            func(&mut self.doc);
+        }
         self
     }
+
+    #[track_caller]
+    fn produces_display(&self, expected: snapbox::data::Inline) -> &Self {
+        assert_data_eq!(self.doc.to_string(), expected.raw());
+        self
+    }
+}
+
+#[test]
+fn test_add_root_decor() {
+    given(
+        r#"[package]
+name = "hello"
+version = "1.0.0"
+
+[[bin]]
+name = "world"
+path = "src/bin/world/main.rs"
+
+[dependencies]
+nom = "4.0" # future is here
+
+[[bin]]
+name = "delete me please"
+path = "src/bin/dmp/main.rs""#,
+    )
+    .running_on_doc(|document| {
+        document.decor_mut().set_prefix("# Some Header\n\n");
+        document.decor_mut().set_suffix("# Some Footer");
+        document.set_trailing("\n\ntrailing...");
+    })
+    .produces_display(str![[r#"
+# Some Header
+
+[package]
+name = "hello"
+version = "1.0.0"
+
+[[bin]]
+name = "world"
+path = "src/bin/world/main.rs"
+
+[dependencies]
+nom = "4.0" # future is here
+
+[[bin]]
+name = "delete me please"
+path = "src/bin/dmp/main.rs"
+# Some Footer
+
+trailing...
+"#]]);
+}
+
+/// Tests that default decor is None for both suffix and prefix and that this means empty strings
+#[test]
+fn test_no_root_decor() {
+    given(
+        r#"[package]
+name = "hello"
+version = "1.0.0"
+
+[[bin]]
+name = "world"
+path = "src/bin/world/main.rs"
+
+[dependencies]
+nom = "4.0" # future is here
+
+[[bin]]
+name = "delete me please"
+path = "src/bin/dmp/main.rs""#,
+    )
+    .running_on_doc(|document| {
+        assert!(document.decor().prefix().is_none());
+        assert!(document.decor().suffix().is_none());
+        document.set_trailing("\n\ntrailing...");
+    })
+    .produces_display(str![[r#"
+[package]
+name = "hello"
+version = "1.0.0"
+
+[[bin]]
+name = "world"
+path = "src/bin/world/main.rs"
+
+[dependencies]
+nom = "4.0" # future is here
+
+[[bin]]
+name = "delete me please"
+path = "src/bin/dmp/main.rs"
+
+
+trailing...
+"#]]);
 }
 
 // insertion
@@ -79,8 +167,8 @@
         root["servers"]["beta"]["ip"] = value("10.0.0.2");
         root["servers"]["beta"]["dc"] = value("eqdc10");
     })
-    .produces_display(
-        r#"[servers]
+    .produces_display(str![[r#"
+[servers]
 
         [servers.alpha]
         ip = "10.0.0.1"
@@ -91,8 +179,8 @@
 dc = "eqdc10"
 
         [other.table]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -108,8 +196,8 @@
     .running(|root| {
         root["dependencies"]["newthing"] = table();
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [package]
         [dependencies]
         [[example]]
@@ -117,18 +205,22 @@
 
 [dependencies.newthing]
         [dev-dependencies]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
 fn test_inserting_tables_from_different_parsed_docs() {
     given("[a]")
         .running(|root| {
-            let other = "[b]".parse::<Document>().unwrap();
+            let other = "[b]".parse::<DocumentMut>().unwrap();
             root["b"] = other["b"].clone();
         })
-        .produces_display("[a]\n[b]\n");
+        .produces_display(str![[r#"
+[a]
+[b]
+
+"#]]);
 }
 #[test]
 fn test_insert_nonleaf_table() {
@@ -142,8 +234,8 @@
         root["servers"]["alpha"]["ip"] = value("10.0.0.1");
         root["servers"]["alpha"]["dc"] = value("eqdc10");
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [other.table]
 
 [servers]
@@ -151,8 +243,8 @@
 [servers.alpha]
 ip = "10.0.0.1"
 dc = "eqdc10"
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -173,8 +265,8 @@
         }
         array.push(Table::new());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [package]
         title = "withoutarray"
 
@@ -182,8 +274,8 @@
 hello = "world"
 
 [[bin]]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -197,15 +289,45 @@
         root["tbl"]["key2"] = value(42);
         root["tbl"]["key3"] = value(8.1415926);
     })
-    .produces_display(
-        r#"[tbl]
+    .produces_display(str![[r#"
+[tbl]
 key1 = "value1"
 key2 = 42
 key3 = 8.1415926
 
         [tbl.son]
-"#,
-    );
+
+"#]]);
+}
+
+#[test]
+fn test_insert_key_with_quotes() {
+    given(
+        r#"
+        [package]
+        name = "foo"
+
+        [target]
+        "#,
+    )
+    .running(|root| {
+        root["target"]["cfg(target_os = \"linux\")"] = table();
+        root["target"]["cfg(target_os = \"linux\")"]["dependencies"] = table();
+        root["target"]["cfg(target_os = \"linux\")"]["dependencies"]["name"] = value("dep");
+    })
+    .produces_display(str![[r#"
+
+        [package]
+        name = "foo"
+
+        [target]
+
+[target.'cfg(target_os = "linux")']
+
+[target.'cfg(target_os = "linux")'.dependencies]
+name = "dep"
+        
+"#]]);
 }
 
 // removal
@@ -230,15 +352,15 @@
         let servers = as_table!(servers);
         assert!(servers.remove("alpha").is_some());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [servers]
 
         [servers.beta]
         ip = "10.0.0.2"
         dc = "eqdc10"
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -281,8 +403,8 @@
     .running(|root| {
         assert!(root.remove("a").is_some());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         title = "not relevant"
         # comment 2
         [b] # comment 2.1
@@ -292,8 +414,7 @@
            [some.other.table]
 
 
-    "#,
-    );
+    "#]]);
 }
 
 #[test]
@@ -323,8 +444,8 @@
         dmp.remove(1);
         assert_eq!(dmp.len(), 1);
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [package]
         name = "hello"
         version = "1.0.0"
@@ -335,8 +456,8 @@
 
         [dependencies]
         nom = "4.0" # future is here
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -361,16 +482,16 @@
     .running(|root| {
         assert!(root.remove("bin").is_some());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [package]
         name = "hello"
         version = "1.0.0"
 
         [dependencies]
         nom = "4.0" # future is here
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -390,14 +511,14 @@
         let value = value.as_value().unwrap();
         assert!(value.is_str());
         let value = value.as_str().unwrap();
-        assert_eq(value, "1.0.0");
+        assert_data_eq!(value, str!["1.0.0"].raw());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         name = "hello"
         documentation = "https://docs.rs/hello"
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -419,7 +540,7 @@
         let value = value.as_value().unwrap();
         assert_eq!(value.as_integer(), Some(1));
     })
-    .produces_display(r#""#);
+    .produces_display(str![]);
 }
 
 // values
@@ -443,8 +564,8 @@
         let a = as_table!(a);
         a.sort_values();
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [a.z]
 
         [a]
@@ -454,8 +575,8 @@
         c = 3
 
         [a.y]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -479,8 +600,8 @@
         // before 'a'.
         a.sort_values_by(|k1, _, k2, _| k1.display_repr().cmp(&k2.display_repr()));
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [a.z]
 
         [a]
@@ -490,8 +611,8 @@
         b = 2 # as well as this
 
         [a.y]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -509,18 +630,18 @@
                 let tab = as_table!(table);
                 tab.set_position(0);
                 let (_, segmented) = tab.iter_mut().next().unwrap();
-                as_table!(segmented).set_position(5)
+                as_table!(segmented).set_position(5);
             }
         }
     })
-    .produces_display(
-        r#"        [dependencies]
+    .produces_display(str![[r#"
+        [dependencies]
 
         [package]
         [dev-dependencies]
         [dependencies.opencl]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -535,18 +656,18 @@
     )
     .running(|root| {
         for (_, table) in root.iter_mut() {
-            as_table!(table).set_position(0)
+            as_table!(table).set_position(0);
         }
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         [package]
         [dependencies]
         [dev-dependencies]
         [dependencies.opencl]
         a=""
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -561,18 +682,18 @@
     )
     .running(|root| {
         for (_, table) in root.iter_mut() {
-            as_table!(table).set_position(usize::MAX)
+            as_table!(table).set_position(usize::MAX);
         }
     })
-    .produces_display(
-        r#"        [dependencies.opencl]
+    .produces_display(str![[r#"
+        [dependencies.opencl]
         a=""
 
         [package]
         [dependencies]
         [dev-dependencies]
-"#,
-    );
+
+"#]]);
 }
 
 macro_rules! as_array {
@@ -623,14 +744,14 @@
         );
         dbg!(root);
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         a = [1, 2, 3, 4]
         b = ["hello", "beep",   "zoink"   ,
 "world"
 ,  "yikes"]
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -654,12 +775,12 @@
         assert!(b.remove(0).is_str());
         assert!(b.is_empty());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         a = [1, 2, 3]
         b = []
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -680,11 +801,10 @@
             }
         }
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
     a = [1, "2", 3.0]
-    "#,
-    );
+    "#]]);
 }
 
 macro_rules! as_inline_table {
@@ -718,14 +838,14 @@
         assert!(b.is_empty());
         b.get_or_insert("hello", "world");
         assert_eq!(b.len(), 1);
-        b.fmt()
+        b.fmt();
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         a = { a = 2, c = 3, b = 42 }
         b = { hello = "world" }
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -749,12 +869,12 @@
         assert!(b.remove("hello").is_some());
         assert!(b.is_empty());
     })
-    .produces_display(
-        r#"
+    .produces_display(str![[r#"
+
         a = {a=2, b = 42}
         b = {}
-"#,
-    );
+
+"#]]);
 }
 
 #[test]
@@ -828,11 +948,11 @@
                 .set_dotted(true);
             root["nixpkgs"]["src"]["git"] = value("https://github.com/nixos/nixpkgs");
         })
-        .produces_display(
-            r#"[nixpkgs]
+        .produces_display(str![[r#"
+[nixpkgs]
 src.git = "https://github.com/nixos/nixpkgs"
-"#,
-        );
+
+"#]]);
 }
 
 #[test]
@@ -847,11 +967,11 @@
                 .unwrap()
                 .set_dotted(true);
         })
-        .produces_display(
-            r#"[nixpkgs]
+        .produces_display(str![[r#"
+[nixpkgs]
 src.git = "https://github.com/nixos/nixpkgs"
-"#,
-        );
+
+"#]]);
 }
 
 #[test]
diff --git a/crates/toml_edit/tests/testsuite/float.rs b/crates/toml_edit/tests/testsuite/float.rs
index 34e792e..20da23f 100644
--- a/crates/toml_edit/tests/testsuite/float.rs
+++ b/crates/toml_edit/tests/testsuite/float.rs
@@ -1,4 +1,4 @@
-use toml_edit::Document;
+use toml_edit::DocumentMut;
 
 macro_rules! float_inf_tests {
     ($ty:ty) => {{
@@ -18,7 +18,7 @@
             sf8 = -0.0
         ";
 
-        let document = document.parse::<Document>().unwrap();
+        let document = document.parse::<DocumentMut>().unwrap();
         let float = |k| document[k].as_float().unwrap();
 
         assert!(float("sf1").is_infinite());
@@ -40,7 +40,7 @@
         assert_eq!(float("sf8"), 0.0);
         assert!(float("sf8").is_sign_negative());
 
-        let mut document = Document::new();
+        let mut document = DocumentMut::new();
         document["sf4"] = toml_edit::value(f64::NAN.copysign(1.0));
         document["sf6"] = toml_edit::value(f64::NAN.copysign(-1.0));
         assert_eq!(
diff --git a/crates/toml_edit/tests/testsuite/invalid.rs b/crates/toml_edit/tests/testsuite/invalid.rs
index cb13b4e..b685ff9 100644
--- a/crates/toml_edit/tests/testsuite/invalid.rs
+++ b/crates/toml_edit/tests/testsuite/invalid.rs
@@ -1,45 +1,58 @@
+use snapbox::assert_data_eq;
+use snapbox::prelude::*;
+use snapbox::str;
+
 #[test]
 fn incomplete_inline_table_issue_296() {
-    let err = "native = {".parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(
-        r#"TOML parse error at line 1, column 11
+    let err = "native = {".parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
+TOML parse error at line 1, column 11
   |
 1 | native = {
   |           ^
 invalid inline table
 expected `}`
-"#,
-        err.to_string(),
+
+"#]]
+        .raw()
     );
 }
 
 #[test]
 fn bare_value_disallowed_issue_293() {
-    let err = "value=zzz".parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(
-        r#"TOML parse error at line 1, column 7
+    let err = "value=zzz".parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
+TOML parse error at line 1, column 7
   |
 1 | value=zzz
   |       ^
 invalid string
 expected `"`, `'`
-"#,
-        err.to_string(),
+
+"#]]
+        .raw()
     );
 }
 
 #[test]
 fn bare_value_in_array_disallowed_issue_293() {
-    let err = "value=[zzz]".parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(
-        r#"TOML parse error at line 1, column 8
+    let err = "value=[zzz]".parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
+TOML parse error at line 1, column 8
   |
 1 | value=[zzz]
   |        ^
 invalid array
 expected `]`
-"#,
-        err.to_string(),
+
+"#]]
+        .raw()
     );
 }
 
@@ -55,157 +68,235 @@
 [dependencies]
 rand = \"0.3.14\"
 "
-    .parse::<toml_edit::Document>()
+    .parse::<toml_edit::DocumentMut>()
     .unwrap_err();
-    snapbox::assert_eq(
-        r#"TOML parse error at line 8, column 1
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
+TOML parse error at line 8, column 1
   |
 8 | [dependencies]
   | ^
 invalid table header
 duplicate key `dependencies` in document root
-"#,
-        err.to_string(),
+
+"#]]
+        .raw()
     );
 }
 
 #[test]
 fn bad() {
     let toml_input = "a = 01";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = 01
   |      ^
 expected newline, `#`
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = 1__1";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = 1__1
   |       ^
 invalid integer
 expected digit
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = 1_";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = 1_
   |       ^
 invalid integer
 expected digit
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "''";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 3
   |
 1 | ''
   |   ^
 expected `.`, `=`
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = 9e99999";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 5
   |
 1 | a = 9e99999
   |     ^
 invalid floating-point number
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = \"\u{7f}\"";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 6
   |
-1 | a = \"\u{7f}\"
+1 | a = ""
   |      ^
 invalid basic string
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = '\u{7f}'";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 6
   |
-1 | a = '\u{7f}'
+1 | a = ''
   |      ^
 invalid literal string
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = -0x1";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = -0x1
   |       ^
 expected newline, `#`
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = 0x-1";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = 0x-1
   |       ^
 invalid hexadecimal integer
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     // Dotted keys.
     let toml_input = "a.b.c = 1
          a.b = 2
         ";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 2, column 10
   |
 2 |          a.b = 2
   |          ^
 duplicate key `b` in document root
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = 1
          a.b = 2";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 2, column 10
   |
 2 |          a.b = 2
   |          ^
 dotted key `a` attempted to extend non-table type (integer)
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
 
     let toml_input = "a = {k1 = 1, k1.name = \"joe\"}";
-    let expected_err = "\
+    let err = toml_input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    assert_data_eq!(
+        err.to_string(),
+        str![[r#"
 TOML parse error at line 1, column 6
   |
-1 | a = {k1 = 1, k1.name = \"joe\"}
+1 | a = {k1 = 1, k1.name = "joe"}
   |      ^
 dotted key `k1` attempted to extend non-table type (integer)
-";
-    let err = toml_input.parse::<toml_edit::Document>().unwrap_err();
-    snapbox::assert_eq(expected_err, err.to_string());
+
+"#]]
+        .raw()
+    );
+}
+
+#[test]
+fn emoji_error_span() {
+    let input = "😀";
+    let err = input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    dbg!(err.span());
+    let actual = &input[err.span().unwrap()];
+    assert_eq!(actual, input);
+}
+
+#[test]
+fn text_error_span() {
+    let input = "asdf";
+    let err = input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    dbg!(err.span());
+    let actual = &input[err.span().unwrap()];
+    assert_eq!(actual, "");
+}
+
+#[test]
+fn fuzzed_68144_error_span() {
+    let input = "\"\\ᾂr\"";
+    let err = input.parse::<toml_edit::DocumentMut>().unwrap_err();
+    dbg!(err.span());
+    let actual = &input[err.span().unwrap()];
+    assert_eq!(actual, "ᾂ");
 }
diff --git a/crates/toml_edit/tests/testsuite/parse.rs b/crates/toml_edit/tests/testsuite/parse.rs
index f22b61d..092d3c3 100644
--- a/crates/toml_edit/tests/testsuite/parse.rs
+++ b/crates/toml_edit/tests/testsuite/parse.rs
@@ -1,5 +1,7 @@
-use snapbox::assert_eq;
-use toml_edit::{Document, Key, Value};
+use snapbox::assert_data_eq;
+use snapbox::prelude::*;
+use snapbox::str;
+use toml_edit::{DocumentMut, Key, Value};
 
 macro_rules! parse {
     ($s:expr, $ty:ty) => {{
@@ -81,24 +83,26 @@
 [a."b".c.e]
 [a.b.c.d]
 "#;
-    let expected = r#"
+    let expected = str![[r#"
+
 [a]
 [a.'b'.c]
 [a.'b'.c.e]
 [a.'b'.c.d]
-"#;
-    let doc = toml.parse::<Document>();
+
+"#]];
+    let doc = toml.parse::<DocumentMut>();
     assert!(doc.is_ok());
     let doc = doc.unwrap();
 
-    assert_eq(expected, doc.to_string());
+    assert_data_eq!(doc.to_string(), expected.raw());
 }
 
 macro_rules! bad {
     ($toml:expr, $msg:expr) => {
-        match $toml.parse::<Document>() {
+        match $toml.parse::<DocumentMut>() {
             Ok(s) => panic!("parsed to: {:#?}", s),
-            Err(e) => snapbox::assert_eq($msg, e.to_string()),
+            Err(e) => assert_data_eq!(e.to_string(), $msg.raw()),
         }
     };
 }
@@ -123,7 +127,7 @@
      contents are never required to be entirely resident in memory all at once.\r\n\
      \"\"\"\
      "
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
 }
 
@@ -163,7 +167,7 @@
 is preserved.
 '''
 "#
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
     assert_eq!(table["bar"].as_str(), Some("\0"));
     assert_eq!(table["key1"].as_str(), Some("One\nTwo"));
@@ -216,7 +220,7 @@
 [foo.bar]
 #...
 "#
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
     table["foo"][0]["bar"].as_table().unwrap();
     table["foo"][1]["bar"].as_table().unwrap();
@@ -226,7 +230,7 @@
 fn empty_table() {
     let table = r#"
 [foo]"#
-        .parse::<Document>()
+        .parse::<DocumentMut>()
         .unwrap();
     table["foo"].as_table().unwrap();
 }
@@ -239,9 +243,9 @@
 
 [package.metadata.release.pre-release-replacements]
 "#;
-    let document = input.parse::<Document>().unwrap();
+    let document = input.parse::<DocumentMut>().unwrap();
     let actual = document.to_string();
-    assert_eq(input, actual);
+    assert_data_eq!(actual, input.raw());
 }
 
 #[test]
@@ -266,7 +270,7 @@
 [[fruit.variety]]
 name = "plantain"
 "#
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
     assert_eq!(table["fruit"][0]["name"].as_str(), Some("apple"));
     assert_eq!(table["fruit"][0]["physical"]["color"].as_str(), Some("red"));
@@ -293,93 +297,99 @@
 fn stray_cr() {
     bad!(
         "\r",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 1
   |
-1 | \r
+1 | 
   | ^
 
-"
+
+"#]]
     );
     bad!(
         "a = [ \r ]",
-        "\
-TOML parse error at line 1, column 7
+        str![[r#"
+TOML parse error at line 1, column 8
   |
-1 | a = [ \r
+1 | a = [ 
  ]
-  |       ^
-invalid array
-expected `]`
-"
+  |        ^
+
+
+"#]]
     );
     bad!(
         "a = \"\"\"\r\"\"\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 8
   |
-1 | a = \"\"\"\r
-\"\"\"
+1 | a = """
+"""
   |        ^
 invalid multiline basic string
-"
+
+"#]]
     );
     bad!(
         "a = \"\"\"\\  \r  \"\"\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
-1 | a = \"\"\"\\  \r
-  \"\"\"
+1 | a = """\  
+  """
   |          ^
 invalid escape sequence
-expected `b`, `f`, `n`, `r`, `t`, `u`, `U`, `\\`, `\"`
-"
+expected `b`, `f`, `n`, `r`, `t`, `u`, `U`, `\`, `"`
+
+"#]]
     );
     bad!(
         "a = '''\r'''",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 8
   |
-1 | a = '''\r
+1 | a = '''
 '''
   |        ^
 invalid multiline literal string
-"
+
+"#]]
     );
     bad!(
         "a = '\r'",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
-1 | a = '\r
+1 | a = '
 '
   |      ^
 invalid literal string
-"
+
+"#]]
     );
     bad!(
         "a = \"\r\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
-1 | a = \"\r
-\"
+1 | a = "
+"
   |      ^
 invalid basic string
-"
+
+"#]]
     );
 }
 
 #[test]
 fn blank_literal_string() {
-    let table = "foo = ''".parse::<Document>().unwrap();
+    let table = "foo = ''".parse::<DocumentMut>().unwrap();
     assert_eq!(table["foo"].as_str(), Some(""));
 }
 
 #[test]
 fn many_blank() {
-    let table = "foo = \"\"\"\n\n\n\"\"\"".parse::<Document>().unwrap();
+    let table = "foo = \"\"\"\n\n\n\"\"\"".parse::<DocumentMut>().unwrap();
     assert_eq!(table["foo"].as_str(), Some("\n\n"));
 }
 
@@ -389,7 +399,7 @@
         foo = \"\"\"\\\r\n\"\"\"
         bar = \"\"\"\\\r\n   \r\n   \r\n   a\"\"\"
     "
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
     assert_eq!(table["foo"].as_str(), Some(""));
     assert_eq!(table["bar"].as_str(), Some("a"));
@@ -399,23 +409,25 @@
 fn string_no_newline() {
     bad!(
         "a = \"\n\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
-1 | a = \"
+1 | a = "
   |      ^
 invalid basic string
-"
+
+"#]]
     );
     bad!(
         "a = '\n'",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = '
   |      ^
 invalid literal string
-"
+
+"#]]
     );
 }
 
@@ -423,83 +435,91 @@
 fn bad_leading_zeros() {
     bad!(
         "a = 00",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = 00
   |      ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "a = -00",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = -00
   |       ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "a = +00",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = +00
   |       ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "a = 00.0",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = 00.0
   |      ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "a = -00.0",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = -00.0
   |       ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "a = +00.0",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = +00.0
   |       ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "a = 9223372036854775808",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 5
   |
 1 | a = 9223372036854775808
   |     ^
 number too large to fit in target type
-"
+
+"#]]
     );
     bad!(
         "a = -9223372036854775809",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 5
   |
 1 | a = -9223372036854775809
   |     ^
 number too small to fit in target type
-"
+
+"#]]
     );
 }
 
@@ -507,76 +527,83 @@
 fn bad_floats() {
     bad!(
         "a = 0.",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = 0.
   |       ^
 invalid floating-point number
 expected digit
-"
+
+"#]]
     );
     bad!(
         "a = 0.e",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = 0.e
   |       ^
 invalid floating-point number
 expected digit
-"
+
+"#]]
     );
     bad!(
         "a = 0.E",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | a = 0.E
   |       ^
 invalid floating-point number
 expected digit
-"
+
+"#]]
     );
     bad!(
         "a = 0.0E",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 9
   |
 1 | a = 0.0E
   |         ^
 invalid floating-point number
-"
+
+"#]]
     );
     bad!(
         "a = 0.0e",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 9
   |
 1 | a = 0.0e
   |         ^
 invalid floating-point number
-"
+
+"#]]
     );
     bad!(
         "a = 0.0e-",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
 1 | a = 0.0e-
   |          ^
 invalid floating-point number
-"
+
+"#]]
     );
     bad!(
         "a = 0.0e+",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
 1 | a = 0.0e+
   |          ^
 invalid floating-point number
-"
+
+"#]]
     );
 }
 
@@ -586,7 +613,7 @@
         ($actual:expr, $expected:expr) => {{
             let f = format!("foo = {}", $actual);
             println!("{}", f);
-            let a = f.parse::<Document>().unwrap();
+            let a = f.parse::<DocumentMut>().unwrap();
             assert_eq!(a["foo"].as_float().unwrap(), $expected);
         }};
     }
@@ -621,7 +648,7 @@
         \"character encoding\" = \"value\"
         'ʎǝʞ' = \"value\"
     "
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
     let _ = &a["foo"];
     let _ = &a["-"];
@@ -640,114 +667,126 @@
 fn bad_keys() {
     bad!(
         "key\n=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | key
   |    ^
 expected `.`, `=`
-"
+
+"#]]
     );
     bad!(
         "key=\n3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 5
   |
 1 | key=
   |     ^
 invalid string
-expected `\"`, `'`
-"
+expected `"`, `'`
+
+"#]]
     );
     bad!(
         "key|=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | key|=3
   |    ^
 expected `.`, `=`
-"
+
+"#]]
     );
     bad!(
         "=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 1
   |
 1 | =3
   | ^
 invalid key
-"
+
+"#]]
     );
     bad!(
         "\"\"|=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
-1 | \"\"|=3
+1 | ""|=3
   |   ^
 expected `.`, `=`
-"
+
+"#]]
     );
     bad!(
         "\"\n\"|=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 2
   |
-1 | \"
+1 | "
   |  ^
 invalid basic string
-"
+
+"#]]
     );
     bad!(
         "\"\r\"|=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 2
   |
-1 | \"\r\"|=3
+1 | "
+"|=3
   |  ^
 invalid basic string
-"
+
+"#]]
     );
     bad!(
         "''''''=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
 1 | ''''''=3
   |   ^
 expected `.`, `=`
-"
+
+"#]]
     );
     bad!(
         "\"\"\"\"\"\"=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
-1 | \"\"\"\"\"\"=3
+1 | """"""=3
   |   ^
 expected `.`, `=`
-"
+
+"#]]
     );
     bad!(
         "'''key'''=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
 1 | '''key'''=3
   |   ^
 expected `.`, `=`
-"
+
+"#]]
     );
     bad!(
         "\"\"\"key\"\"\"=3",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
-1 | \"\"\"key\"\"\"=3
+1 | """key"""=3
   |   ^
 expected `.`, `=`
-"
+
+"#]]
     );
 }
 
@@ -755,139 +794,152 @@
 fn bad_table_names() {
     bad!(
         "[]",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 2
   |
 1 | []
   |  ^
 invalid key
-"
+
+"#]]
     );
     bad!(
         "[.]",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 2
   |
 1 | [.]
   |  ^
 invalid key
-"
+
+"#]]
     );
     bad!(
         "[a.]",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
 1 | [a.]
   |   ^
 invalid table header
 expected `.`, `]`
-"
+
+"#]]
     );
     bad!(
         "[!]",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 2
   |
 1 | [!]
   |  ^
 invalid key
-"
+
+"#]]
     );
     bad!(
         "[\"\n\"]",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
-1 | [\"
+1 | ["
   |   ^
 invalid basic string
-"
+
+"#]]
     );
     bad!(
         "[a.b]\n[a.\"b\"]",
-        "\
+        str![[r#"
 TOML parse error at line 2, column 1
   |
-2 | [a.\"b\"]
+2 | [a."b"]
   | ^
 invalid table header
 duplicate key `b` in table `a`
-"
+
+"#]]
     );
     bad!(
         "[']",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | [']
   |    ^
 invalid literal string
-"
+
+"#]]
     );
     bad!(
         "[''']",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | [''']
   |    ^
 invalid table header
 expected `.`, `]`
-"
+
+"#]]
     );
     bad!(
         "['''''']",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | ['''''']
   |    ^
 invalid table header
 expected `.`, `]`
-"
+
+"#]]
     );
     bad!(
         "['''foo''']",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | ['''foo''']
   |    ^
 invalid table header
 expected `.`, `]`
-"
+
+"#]]
     );
     bad!(
         "[\"\"\"bar\"\"\"]",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
-1 | [\"\"\"bar\"\"\"]
+1 | ["""bar"""]
   |    ^
 invalid table header
 expected `.`, `]`
-"
+
+"#]]
     );
     bad!(
         "['\n']",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
 1 | ['
   |   ^
 invalid literal string
-"
+
+"#]]
     );
     bad!(
         "['\r\n']",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 3
   |
 1 | ['
   |   ^
 invalid literal string
-"
+
+"#]]
     );
 }
 
@@ -901,7 +953,7 @@
         ['a.a']
         ['\"\"']
     "
-    .parse::<Document>()
+    .parse::<DocumentMut>()
     .unwrap();
     println!("{:?}", a);
     let _ = &a["a"]["b"];
@@ -915,82 +967,88 @@
 fn invalid_bare_numeral() {
     bad!(
         "4",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 2
   |
 1 | 4
   |  ^
 expected `.`, `=`
-"
+
+"#]]
     );
 }
 
 #[test]
 fn inline_tables() {
-    "a = {}".parse::<Document>().unwrap();
-    "a = {b=1}".parse::<Document>().unwrap();
-    "a = {   b   =   1    }".parse::<Document>().unwrap();
-    "a = {a=1,b=2}".parse::<Document>().unwrap();
-    "a = {a=1,b=2,c={}}".parse::<Document>().unwrap();
+    "a = {}".parse::<DocumentMut>().unwrap();
+    "a = {b=1}".parse::<DocumentMut>().unwrap();
+    "a = {   b   =   1    }".parse::<DocumentMut>().unwrap();
+    "a = {a=1,b=2}".parse::<DocumentMut>().unwrap();
+    "a = {a=1,b=2,c={}}".parse::<DocumentMut>().unwrap();
 
     bad!(
         "a = {a=1,}",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 9
   |
 1 | a = {a=1,}
   |         ^
 invalid inline table
 expected `}`
-"
+
+"#]]
     );
     bad!(
         "a = {,}",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = {,}
   |      ^
 invalid inline table
 expected `}`
-"
+
+"#]]
     );
     bad!(
         "a = {a=1,a=1}",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = {a=1,a=1}
   |      ^
 duplicate key `a`
-"
+
+"#]]
     );
     bad!(
         "a = {\n}",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = {
   |      ^
 invalid inline table
 expected `}`
-"
+
+"#]]
     );
     bad!(
         "a = {",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 6
   |
 1 | a = {
   |      ^
 invalid inline table
 expected `}`
-"
+
+"#]]
     );
 
-    "a = {a=[\n]}".parse::<Document>().unwrap();
-    "a = {\"a\"=[\n]}".parse::<Document>().unwrap();
-    "a = [\n{},\n{},\n]".parse::<Document>().unwrap();
+    "a = {a=[\n]}".parse::<DocumentMut>().unwrap();
+    "a = {\"a\"=[\n]}".parse::<DocumentMut>().unwrap();
+    "a = [\n{},\n{},\n]".parse::<DocumentMut>().unwrap();
 }
 
 #[test]
@@ -998,7 +1056,7 @@
     macro_rules! t {
         ($actual:expr, $expected:expr) => {{
             let f = format!("foo = {}", $actual);
-            let table = f.parse::<Document>().unwrap();
+            let table = f.parse::<DocumentMut>().unwrap();
             assert_eq!(table["foo"].as_integer().unwrap(), $expected);
         }};
     }
@@ -1014,45 +1072,49 @@
 fn bad_underscores() {
     bad!(
         "foo = 0_",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 8
   |
 1 | foo = 0_
   |        ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = 0__0",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 8
   |
 1 | foo = 0__0
   |        ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = __0",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | foo = __0
   |       ^
 invalid integer
 expected leading digit
-"
+
+"#]]
     );
     bad!(
         "foo = 1_0_",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 11
   |
 1 | foo = 1_0_
   |           ^
 invalid integer
 expected digit
-"
+
+"#]]
     );
 }
 
@@ -1060,14 +1122,15 @@
 fn bad_unicode_codepoint() {
     bad!(
         "foo = \"\\uD800\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
-1 | foo = \"\\uD800\"
+1 | foo = "\uD800"
   |          ^
 invalid unicode 4-digit hex code
 value is out of range
-"
+
+"#]]
     );
 }
 
@@ -1075,50 +1138,54 @@
 fn bad_strings() {
     bad!(
         "foo = \"\\uxx\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
-1 | foo = \"\\uxx\"
+1 | foo = "\uxx"
   |          ^
 invalid unicode 4-digit hex code
-"
+
+"#]]
     );
     bad!(
         "foo = \"\\u\"",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 10
   |
-1 | foo = \"\\u\"
+1 | foo = "\u"
   |          ^
 invalid unicode 4-digit hex code
-"
+
+"#]]
     );
     bad!(
         "foo = \"\\",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 8
   |
-1 | foo = \"\\
+1 | foo = "\
   |        ^
 invalid basic string
-"
+
+"#]]
     );
     bad!(
         "foo = '",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 8
   |
 1 | foo = '
   |        ^
 invalid literal string
-"
+
+"#]]
     );
 }
 
 #[test]
 fn empty_string() {
     assert_eq!(
-        "foo = \"\"".parse::<Document>().unwrap()["foo"]
+        "foo = \"\"".parse::<DocumentMut>().unwrap()["foo"]
             .as_str()
             .unwrap(),
         ""
@@ -1127,53 +1194,57 @@
 
 #[test]
 fn booleans() {
-    let table = "foo = true".parse::<Document>().unwrap();
+    let table = "foo = true".parse::<DocumentMut>().unwrap();
     assert_eq!(table["foo"].as_bool(), Some(true));
 
-    let table = "foo = false".parse::<Document>().unwrap();
+    let table = "foo = false".parse::<DocumentMut>().unwrap();
     assert_eq!(table["foo"].as_bool(), Some(false));
 
     bad!(
         "foo = true2",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 11
   |
 1 | foo = true2
   |           ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = false2",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 12
   |
 1 | foo = false2
   |            ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = t1",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | foo = t1
   |       ^
 invalid string
-expected `\"`, `'`
-"
+expected `"`, `'`
+
+"#]]
     );
     bad!(
         "foo = f2",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 7
   |
 1 | foo = f2
   |       ^
 invalid string
-expected `\"`, `'`
-"
+expected `"`, `'`
+
+"#]]
     );
 }
 
@@ -1185,56 +1256,60 @@
         [[a]]
         b = 5
         ",
-        "\
+        str![[r#"
 TOML parse error at line 3, column 9
   |
 3 |         [[a]]
   |         ^
 invalid table header
 duplicate key `a` in document root
-"
+
+"#]]
     );
     bad!(
         "
         a = 1
         [a.b]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 3, column 9
   |
 3 |         [a.b]
   |         ^
 invalid table header
 dotted key `a` attempted to extend non-table type (integer)
-"
+
+"#]]
     );
     bad!(
         "
         a = []
         [a.b]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 3, column 9
   |
 3 |         [a.b]
   |         ^
 invalid table header
 dotted key `a` attempted to extend non-table type (array)
-"
+
+"#]]
     );
     bad!(
         "
         a = []
         [[a.b]]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 3, column 9
   |
 3 |         [[a.b]]
   |         ^
 invalid table header
 dotted key `a` attempted to extend non-table type (array)
-"
+
+"#]]
     );
     bad!(
         "
@@ -1243,14 +1318,15 @@
         [a.b]
         c = 2
         ",
-        "\
+        str![[r#"
 TOML parse error at line 4, column 9
   |
 4 |         [a.b]
   |         ^
 invalid table header
 duplicate key `b` in table `a`
-"
+
+"#]]
     );
 }
 
@@ -1264,14 +1340,15 @@
         foo=\"bar\"
         [a]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 6, column 9
   |
 6 |         [a]
   |         ^
 invalid table header
 duplicate key `a` in document root
-"
+
+"#]]
     );
     bad!(
         "
@@ -1280,14 +1357,15 @@
         b = { foo = \"bar\" }
         [a]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 5, column 9
   |
 5 |         [a]
   |         ^
 invalid table header
 duplicate key `a` in document root
-"
+
+"#]]
     );
     bad!(
         "
@@ -1295,14 +1373,15 @@
         b = {}
         [a.b]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 4, column 9
   |
 4 |         [a.b]
   |         ^
 invalid table header
 duplicate key `b` in table `a`
-"
+
+"#]]
     );
 
     bad!(
@@ -1311,14 +1390,15 @@
         b = {}
         [a]
         ",
-        "\
+        str![[r#"
 TOML parse error at line 4, column 9
   |
 4 |         [a]
   |         ^
 invalid table header
 duplicate key `a` in document root
-"
+
+"#]]
     );
 }
 
@@ -1327,7 +1407,7 @@
     macro_rules! t {
         ($actual:expr) => {{
             let f = format!("foo = {}", $actual);
-            let toml = f.parse::<Document>().expect(&format!("failed: {}", f));
+            let toml = f.parse::<DocumentMut>().expect(&format!("failed: {}", f));
             assert_eq!(toml["foo"].as_datetime().unwrap().to_string(), $actual);
         }};
     }
@@ -1338,53 +1418,58 @@
     t!("2016-09-09T09:09:09.123456789-02:00");
     bad!(
         "foo = 2016-09-09T09:09:09.Z",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 26
   |
 1 | foo = 2016-09-09T09:09:09.Z
   |                          ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         "foo = 2016-9-09T09:09:09Z",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 12
   |
 1 | foo = 2016-9-09T09:09:09Z
   |            ^
 invalid date-time
-"
+
+"#]]
     );
     bad!(
         "foo = 2016-09-09T09:09:09+2:00",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 27
   |
 1 | foo = 2016-09-09T09:09:09+2:00
   |                           ^
 invalid time offset
-"
+
+"#]]
     );
     bad!(
         "foo = 2016-09-09T09:09:09-2:00",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 27
   |
 1 | foo = 2016-09-09T09:09:09-2:00
   |                           ^
 invalid time offset
-"
+
+"#]]
     );
     bad!(
         "foo = 2016-09-09T09:09:09Z-2:00",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 27
   |
 1 | foo = 2016-09-09T09:09:09Z-2:00
   |                           ^
 expected newline, `#`
-"
+
+"#]]
     );
 }
 
@@ -1392,24 +1477,27 @@
 fn require_newline_after_value() {
     bad!(
         "0=0r=false",
-        "\
+        str![[r#"
 TOML parse error at line 1, column 4
   |
 1 | 0=0r=false
   |    ^
 expected newline, `#`
-"
+
+"#]]
     );
     bad!(
         r#"
 0=""o=""m=""r=""00="0"q="""0"""e="""0"""
 "#,
-        r#"TOML parse error at line 2, column 5
+        str![[r#"
+TOML parse error at line 2, column 5
   |
 2 | 0=""o=""m=""r=""00="0"q="""0"""e="""0"""
   |     ^
 expected newline, `#`
-"#
+
+"#]]
     );
     bad!(
         r#"
@@ -1418,45 +1506,53 @@
 0="0"[[0000l0]]
 0="0"l="0"
 "#,
-        r#"TOML parse error at line 3, column 6
+        str![[r#"
+TOML parse error at line 3, column 6
   |
 3 | 0="0"[[0000l0]]
   |      ^
 expected newline, `#`
-"#
+
+"#]]
     );
     bad!(
         r#"
 0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z]
 "#,
-        r#"TOML parse error at line 2, column 6
+        str![[r#"
+TOML parse error at line 2, column 6
   |
 2 | 0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z]
   |      ^
 expected newline, `#`
-"#
+
+"#]]
     );
     bad!(
         r#"
 0=0r0=0r=false
 "#,
-        r#"TOML parse error at line 2, column 4
+        str![[r#"
+TOML parse error at line 2, column 4
   |
 2 | 0=0r0=0r=false
   |    ^
 expected newline, `#`
-"#
+
+"#]]
     );
     bad!(
         r#"
 0=0r0=0r=falsefal=false
 "#,
-        r#"TOML parse error at line 2, column 4
+        str![[r#"
+TOML parse error at line 2, column 4
   |
 2 | 0=0r0=0r=falsefal=false
   |    ^
 expected newline, `#`
-"#
+
+"#]]
     );
 }
 
@@ -1471,14 +1567,14 @@
 p.a=4
 [p.o]
 "#;
-    let document = input.parse::<Document>().unwrap();
+    let document = input.parse::<DocumentMut>().unwrap();
     let actual = document.to_string();
-    assert_eq(input, actual);
+    assert_data_eq!(actual, input.raw());
 }
 
 #[test]
 fn despan_keys() {
-    let mut doc = r#"aaaaaa = 1"#.parse::<Document>().unwrap();
+    let mut doc = r#"aaaaaa = 1"#.parse::<DocumentMut>().unwrap();
     let key = "bbb".parse::<Key>().unwrap();
     let table = doc.as_table_mut();
     table.insert_formatted(
@@ -1503,8 +1599,260 @@
 "###;
     let expected = input;
 
-    let manifest: toml_edit::Document = input.parse().unwrap();
+    let manifest: DocumentMut = input.parse().unwrap();
     let actual = manifest.to_string();
 
-    assert_eq(expected, actual);
+    assert_data_eq!(actual, expected.raw());
+}
+
+#[test]
+fn string_repr_roundtrip() {
+    assert_string_repr_roundtrip(r#""""#, str![[r#""""#]]);
+    assert_string_repr_roundtrip(r#""a""#, str![[r#""a""#]]);
+
+    assert_string_repr_roundtrip(r#""tab \t tab""#, str![[r#""tab /t tab""#]]);
+    assert_string_repr_roundtrip(r#""lf \n lf""#, str![[r#""lf /n lf""#]]);
+    assert_string_repr_roundtrip(r#""crlf \r\n crlf""#, str![[r#""crlf /r/n crlf""#]]);
+    assert_string_repr_roundtrip(r#""bell \b bell""#, str![[r#""bell /b bell""#]]);
+    assert_string_repr_roundtrip(r#""feed \f feed""#, str![[r#""feed /f feed""#]]);
+    assert_string_repr_roundtrip(
+        r#""backslash \\ backslash""#,
+        str![[r#""backslash // backslash""#]],
+    );
+
+    assert_string_repr_roundtrip(r#""squote ' squote""#, str![[r#""squote ' squote""#]]);
+    assert_string_repr_roundtrip(
+        r#""triple squote ''' triple squote""#,
+        str![[r#""triple squote ''' triple squote""#]],
+    );
+    assert_string_repr_roundtrip(r#""end squote '""#, str![[r#""end squote '""#]]);
+
+    assert_string_repr_roundtrip(r#""quote \" quote""#, str![[r#""quote /" quote""#]]);
+    assert_string_repr_roundtrip(
+        r#""triple quote \"\"\" triple quote""#,
+        str![[r#""triple quote /"/"/" triple quote""#]],
+    );
+    assert_string_repr_roundtrip(r#""end quote \"""#, str![[r#""end quote /"""#]]);
+    assert_string_repr_roundtrip(
+        r#""quoted \"content\" quoted""#,
+        str![[r#""quoted /"content/" quoted""#]],
+    );
+    assert_string_repr_roundtrip(
+        r#""squoted 'content' squoted""#,
+        str![[r#""squoted 'content' squoted""#]],
+    );
+    assert_string_repr_roundtrip(
+        r#""mixed quoted \"start\" 'end'' mixed quote""#,
+        str![[r#""mixed quoted /"start/" 'end'' mixed quote""#]],
+    );
+}
+
+#[track_caller]
+fn assert_string_repr_roundtrip(input: &str, expected: impl IntoData) {
+    let value: Value = input.parse().unwrap();
+    let actual = value.to_string();
+    let _: Value = actual.parse().unwrap_or_else(|_err| {
+        panic!(
+            "invalid `Value`:
+```
+{actual}
+```
+"
+        )
+    });
+    let expected = expected.into_data();
+    assert_data_eq!(actual, expected);
+}
+
+#[test]
+fn string_value_roundtrip() {
+    assert_string_value_roundtrip(r#""""#, str![[r#""""#]]);
+    assert_string_value_roundtrip(r#""a""#, str![[r#""a""#]]);
+
+    assert_string_value_roundtrip(r#""tab \t tab""#, str![[r#""tab /t tab""#]]);
+    assert_string_value_roundtrip(
+        r#""lf \n lf""#,
+        str![[r#"
+"""
+lf 
+ lf"""
+"#]],
+    );
+    assert_string_value_roundtrip(
+        r#""crlf \r\n crlf""#,
+        str![[r#"
+"""
+crlf /r
+ crlf"""
+"#]],
+    );
+    assert_string_value_roundtrip(r#""bell \b bell""#, str![[r#""bell /b bell""#]]);
+    assert_string_value_roundtrip(r#""feed \f feed""#, str![[r#""feed /f feed""#]]);
+    assert_string_value_roundtrip(
+        r#""backslash \\ backslash""#,
+        str!["'backslash / backslash'"],
+    );
+
+    assert_string_value_roundtrip(r#""squote ' squote""#, str![[r#""squote ' squote""#]]);
+    assert_string_value_roundtrip(
+        r#""triple squote ''' triple squote""#,
+        str![[r#""triple squote ''' triple squote""#]],
+    );
+    assert_string_value_roundtrip(r#""end squote '""#, str![[r#""end squote '""#]]);
+
+    assert_string_value_roundtrip(r#""quote \" quote""#, str![[r#"'quote " quote'"#]]);
+    assert_string_value_roundtrip(
+        r#""triple quote \"\"\" triple quote""#,
+        str![[r#"'triple quote """ triple quote'"#]],
+    );
+    assert_string_value_roundtrip(r#""end quote \"""#, str![[r#"'end quote "'"#]]);
+    assert_string_value_roundtrip(
+        r#""quoted \"content\" quoted""#,
+        str![[r#"'quoted "content" quoted'"#]],
+    );
+    assert_string_value_roundtrip(
+        r#""squoted 'content' squoted""#,
+        str![[r#""squoted 'content' squoted""#]],
+    );
+    assert_string_value_roundtrip(
+        r#""mixed quoted \"start\" 'end'' mixed quote""#,
+        str![[r#"'''mixed quoted "start" 'end'' mixed quote'''"#]],
+    );
+}
+
+#[track_caller]
+fn assert_string_value_roundtrip(input: &str, expected: impl IntoData) {
+    let value: Value = input.parse().unwrap();
+    let value = Value::from(value.as_str().unwrap()); // Remove repr
+    let actual = value.to_string();
+    let _: Value = actual.parse().unwrap_or_else(|_err| {
+        panic!(
+            "invalid `Value`:
+```
+{actual}
+```
+"
+        )
+    });
+    let expected = expected.into_data();
+    assert_data_eq!(actual, expected);
+}
+
+#[test]
+fn key_repr_roundtrip() {
+    assert_key_repr_roundtrip(r#""""#, str![[r#""""#]]);
+    assert_key_repr_roundtrip(r#""a""#, str![[r#""a""#]]);
+
+    assert_key_repr_roundtrip(r#""tab \t tab""#, str![[r#""tab /t tab""#]]);
+    assert_key_repr_roundtrip(r#""lf \n lf""#, str![[r#""lf /n lf""#]]);
+    assert_key_repr_roundtrip(r#""crlf \r\n crlf""#, str![[r#""crlf /r/n crlf""#]]);
+    assert_key_repr_roundtrip(r#""bell \b bell""#, str![[r#""bell /b bell""#]]);
+    assert_key_repr_roundtrip(r#""feed \f feed""#, str![[r#""feed /f feed""#]]);
+    assert_key_repr_roundtrip(
+        r#""backslash \\ backslash""#,
+        str![[r#""backslash // backslash""#]],
+    );
+
+    assert_key_repr_roundtrip(r#""squote ' squote""#, str![[r#""squote ' squote""#]]);
+    assert_key_repr_roundtrip(
+        r#""triple squote ''' triple squote""#,
+        str![[r#""triple squote ''' triple squote""#]],
+    );
+    assert_key_repr_roundtrip(r#""end squote '""#, str![[r#""end squote '""#]]);
+
+    assert_key_repr_roundtrip(r#""quote \" quote""#, str![[r#""quote /" quote""#]]);
+    assert_key_repr_roundtrip(
+        r#""triple quote \"\"\" triple quote""#,
+        str![[r#""triple quote /"/"/" triple quote""#]],
+    );
+    assert_key_repr_roundtrip(r#""end quote \"""#, str![[r#""end quote /"""#]]);
+    assert_key_repr_roundtrip(
+        r#""quoted \"content\" quoted""#,
+        str![[r#""quoted /"content/" quoted""#]],
+    );
+    assert_key_repr_roundtrip(
+        r#""squoted 'content' squoted""#,
+        str![[r#""squoted 'content' squoted""#]],
+    );
+    assert_key_repr_roundtrip(
+        r#""mixed quoted \"start\" 'end'' mixed quote""#,
+        str![[r#""mixed quoted /"start/" 'end'' mixed quote""#]],
+    );
+}
+
+#[track_caller]
+fn assert_key_repr_roundtrip(input: &str, expected: impl IntoData) {
+    let value: Key = input.parse().unwrap();
+    let actual = value.to_string();
+    let _: Key = actual.parse().unwrap_or_else(|_err| {
+        panic!(
+            "invalid `Key`:
+```
+{actual}
+```
+"
+        )
+    });
+    let expected = expected.into_data();
+    assert_data_eq!(actual, expected);
+}
+
+#[test]
+fn key_value_roundtrip() {
+    assert_key_value_roundtrip(r#""""#, str![[r#""""#]]);
+    assert_key_value_roundtrip(r#""a""#, str!["a"]);
+
+    assert_key_value_roundtrip(r#""tab \t tab""#, str![[r#""tab /t tab""#]]);
+    assert_key_value_roundtrip(r#""lf \n lf""#, str![[r#""lf /n lf""#]]);
+    assert_key_value_roundtrip(r#""crlf \r\n crlf""#, str![[r#""crlf /r/n crlf""#]]);
+    assert_key_value_roundtrip(r#""bell \b bell""#, str![[r#""bell /b bell""#]]);
+    assert_key_value_roundtrip(r#""feed \f feed""#, str![[r#""feed /f feed""#]]);
+    assert_key_value_roundtrip(
+        r#""backslash \\ backslash""#,
+        str!["'backslash / backslash'"],
+    );
+
+    assert_key_value_roundtrip(r#""squote ' squote""#, str![[r#""squote ' squote""#]]);
+    assert_key_value_roundtrip(
+        r#""triple squote ''' triple squote""#,
+        str![[r#""triple squote ''' triple squote""#]],
+    );
+    assert_key_value_roundtrip(r#""end squote '""#, str![[r#""end squote '""#]]);
+
+    assert_key_value_roundtrip(r#""quote \" quote""#, str![[r#"'quote " quote'"#]]);
+    assert_key_value_roundtrip(
+        r#""triple quote \"\"\" triple quote""#,
+        str![[r#"'triple quote """ triple quote'"#]],
+    );
+    assert_key_value_roundtrip(r#""end quote \"""#, str![[r#"'end quote "'"#]]);
+    assert_key_value_roundtrip(
+        r#""quoted \"content\" quoted""#,
+        str![[r#"'quoted "content" quoted'"#]],
+    );
+    assert_key_value_roundtrip(
+        r#""squoted 'content' squoted""#,
+        str![[r#""squoted 'content' squoted""#]],
+    );
+    assert_key_value_roundtrip(
+        r#""mixed quoted \"start\" 'end'' mixed quote""#,
+        str![[r#""mixed quoted /"start/" 'end'' mixed quote""#]],
+    );
+}
+
+#[track_caller]
+fn assert_key_value_roundtrip(input: &str, expected: impl IntoData) {
+    let value: Key = input.parse().unwrap();
+    let value = Key::new(value.get()); // Remove repr
+    let actual = value.to_string();
+    let _: Key = actual.parse().unwrap_or_else(|_err| {
+        panic!(
+            "invalid `Key`:
+```
+{actual}
+```
+"
+        )
+    });
+    let expected = expected.into_data();
+    assert_data_eq!(actual, expected);
 }
diff --git a/crates/toml_edit/tests/testsuite/stackoverflow.rs b/crates/toml_edit/tests/testsuite/stackoverflow.rs
index de4c722..2e6d246 100644
--- a/crates/toml_edit/tests/testsuite/stackoverflow.rs
+++ b/crates/toml_edit/tests/testsuite/stackoverflow.rs
@@ -4,7 +4,7 @@
     let depths = [(1, true), (20, true), (300, false)];
     for (depth, is_ok) in depths {
         let input = format!("x={}{}", &"[".repeat(depth), &"]".repeat(depth));
-        let document = input.parse::<toml_edit::Document>();
+        let document = input.parse::<toml_edit::DocumentMut>();
         assert_eq!(document.is_ok(), is_ok, "depth: {}", depth);
     }
 }
@@ -15,7 +15,7 @@
     let depths = [(1, true), (20, true), (300, false)];
     for (depth, is_ok) in depths {
         let input = format!("x={}true{}", &"{ x = ".repeat(depth), &"}".repeat(depth));
-        let document = input.parse::<toml_edit::Document>();
+        let document = input.parse::<toml_edit::DocumentMut>();
         assert_eq!(document.is_ok(), is_ok, "depth: {}", depth);
     }
 }
@@ -26,7 +26,7 @@
     let depths = [(1, true), (20, true), (300, false)];
     for (depth, is_ok) in depths {
         let input = format!("[x{}]", &".x".repeat(depth));
-        let document = input.parse::<toml_edit::Document>();
+        let document = input.parse::<toml_edit::DocumentMut>();
         assert_eq!(document.is_ok(), is_ok, "depth: {}", depth);
     }
 }
@@ -37,7 +37,7 @@
     let depths = [(1, true), (20, true), (300, false)];
     for (depth, is_ok) in depths {
         let input = format!("x{} = true", &".x".repeat(depth));
-        let document = input.parse::<toml_edit::Document>();
+        let document = input.parse::<toml_edit::DocumentMut>();
         assert_eq!(document.is_ok(), is_ok, "depth: {}", depth);
     }
 }
@@ -48,7 +48,7 @@
     let depths = [(1, true), (20, true), (300, false)];
     for (depth, is_ok) in depths {
         let input = format!("x = {{ x{} = true }}", &".x".repeat(depth));
-        let document = input.parse::<toml_edit::Document>();
+        let document = input.parse::<toml_edit::DocumentMut>();
         assert_eq!(document.is_ok(), is_ok, "depth: {}", depth);
     }
 }
diff --git a/crates/winnow/.android-checksum.json b/crates/winnow/.android-checksum.json
index 930ab63..a791faf 100644
--- a/crates/winnow/.android-checksum.json
+++ b/crates/winnow/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"9807c51d899dbd4514e2e6363f5434bccee13cce357e4baa61ce3c966c68e9fa","Android.bp":"c97eda5127e5f3cd2248e5d2067ca9c240ee6f35c90b8662765f1493ed5474dd","Cargo.lock":"1b5c9eaab0f7efffefe7761a8683d3084dba064809874b9300c136b19a94960c","Cargo.toml":"0ac50b2e08a91c98a006442d12cfd7dd30733da1cb7ae5d6a242ac28a932a01c","LICENSE":"c0eb8549d2cf81653902a4df95bcbf95657d17ac97ee17d4ac99409be3d02d09","LICENSE-MIT":"c0eb8549d2cf81653902a4df95bcbf95657d17ac97ee17d4ac99409be3d02d09","METADATA":"6dda3988202652161cea8e3149d3a021347df5d0e542cf64ad927e31a2adbfe0","MODULE_LICENSE_MIT":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"dab283945bcaeeab1ede8ee4521016c740bfa62b27385f4aedb72ebf38e3a68d","benches/contains_token.rs":"0f11db56776001e13ee9058ac7e2a31832d838bf83a3e0bc850d23bee2a5254c","benches/find_slice.rs":"8b0f978cde8992af1895bb0cf984e4c0b0dc6558f78080b167a58e6a40ced873","benches/iter.rs":"4e3a64f6c3ae1efd5311033a23241472b85a5bbf1eacc180443f197ec221ab66","benches/next_slice.rs":"5a9825b165a5afe6858cd49732f737638bb35a3c41fcc97f3a8b2bb9e8582bb0","benches/number.rs":"48898f827b7545f9de081a94aa95715cc3dbae4f36345ff23d4b4f8a020607d3","cargo_embargo.json":"4322d11531d0decc7f497f1c135679c7f1573c2362137121d587bf6d49e21fcb","examples/arithmetic/bench.rs":"58d7c0e48a39d2f439adcbe75e5d537f036646835b712935d14478e2cab2f78a","examples/arithmetic/main.rs":"80d7b17eef961eb010b9c40260b76c93a8470142ff1533358fc4fc83e71c23d2","examples/arithmetic/parser.rs":"73174a9aa5165ebb9451b0b77736db3a5d7fef45e8dd9d50293d2495fae5a7d3","examples/arithmetic/parser_ast.rs":"3d3e9cfbc4d6250778615fed07b4820bea27f07cb94580faca29357e87062d12","examples/arithmetic/parser_lexer.rs":"24a21c2b444a06b70db8e645182a2ce63cd2c80c824559a96af960a8faad506d","examples/css/main.rs":"a3076c828130b4382d95e3f5de692bd7cf0eebb2bddb76077b88287b3490de31","examples/css/parser.rs":"94ad11cbfa9baeb67eddf9cbfe7a9ce955bdd9c0624996f822c928b460d0461d","examples/custom_error.rs":"86836f16fc8cf5245964236d12d8a4a89abe908f8aab7c56632a4a3b8776c0e4","examples/http/bench.rs":"1e5d38c6411b3069bf7a998269fab94da79835c8c0fe9118b3dd271926603133","examples/http/main.rs":"4e70aa42228e684c43dce8849ffba3259638e426e8adbeb97d049f3d761b37c6","examples/http/parser.rs":"d63fa668cfbe22253baf0d78a5da617054808788ddf10ba0bb978bb738dc0820","examples/http/parser_streaming.rs":"07146bb496b7dfc74c0a3129c38652b81d62942423f92a32e09e5450e30d6c3a","examples/ini/bench.rs":"9c2a733b5d40b809066c5b157012c4e2016de3bc03a3fa266bda9a5fbddb2233","examples/ini/main.rs":"0d567bca2b3c80932d42c783bafdaa348d97f033438afaf67215d9187ff95c4b","examples/ini/parser.rs":"3b2f7365b328cb2c7e15a043bf94cb9ac946d381112e18bdbde239a6487d6254","examples/ini/parser_str.rs":"d6989e083a25caac703c0d988d7f0012d250033d6a369c6efef06cd4bab26663","examples/iterator.rs":"315d5d88cca72216574e6880da2148eb61a23d7cb6312c4b2f8181fce56fd358","examples/json/bench.rs":"6b7722681f3dad5b7fbbfa8519abd22ca4096ed12fa83c1101092f852e1a76a5","examples/json/json.rs":"353c6b044f18e932a9079a2db850593df3a5e1bd08ec3c8da3d227fd12b64fbc","examples/json/main.rs":"4977e3b0db27033d65b119585b453134fadf5f36ceac42ab6d46d6d03b4534ef","examples/json/parser.rs":"2c5e52c945fce159eb76921988ef2b27f7d3b72e4c74233ff09bb32f0e6c645e","examples/json/parser_dispatch.rs":"55362b0d8a64623085b2b408e65a1657b2ab8ec0392994e89e8643157f90b662","examples/json/parser_partial.rs":"32d96bb3afd93a7b027fb51cca890bae887340983f36d4b5160e6775d52f1486","examples/json_iterator.rs":"c73125de3c73149cac6436781ba6449b1d530beb9196e68d2fea77faf4d528bf","examples/ndjson/example.ndjson":"1c013270a2f5ee7d2df2ac6fa1b9d82af68afe954c7a0ed629c04e274d6f7bac","examples/ndjson/main.rs":"50aeeb26a11a6445e8e58c5b84c4b9282b3155dacbd782f0609773d9f17a0038","examples/ndjson/parser.rs":"e1e33c479540fd3c23e4725364c9f0aa5b4e45dc90767e46e0d73b8862e1fad6","examples/s_expression/main.rs":"2e365d7c5e5dbcddfcc4bdc0d73407ed041d270b524125c04e89ec9925c0274e","examples/s_expression/parser.rs":"b54f415141d5aa90fdb98178e006e245519828d71a065b72b9ce26413d5b9809","examples/string/main.rs":"4b87cfb442ce6251981ade950ca92e0da7ad4b38bb9fc9c6935d797106d69843","examples/string/parser.rs":"e997fb0e78c4c982c4267e1cac29d551c05eb45e9bf4ba6a855a479ab6add1ce","src/_topic/arithmetic.rs":"b79c44c429021dc2f4ce8bd2821b53eed1d65bfe47ba91bd036c17f748e4ceba","src/_topic/error.rs":"b77f2c21f5290f3bd2f544b704b7885b2c8c171fb4f7ff5cab5b92f0f7dacde9","src/_topic/fromstr.rs":"aa7b4d94824efd91a6db87e5477f98f19ac20f931d65d09f064b167b424e8ed6","src/_topic/http.rs":"b4a0854c82f687cd4307b0ba1aba448a1a73cb06409a521b512735900a2a121d","src/_topic/ini.rs":"f7dc4e1c4d824597317e09a5a8737ac5b8578094d616d4cc9aa8367cbeb1747f","src/_topic/json.rs":"559d2a9d7b7461eaf92b6985a0e79f54c32a63c70ce2ea86a2528413e1f2ddd4","src/_topic/language.rs":"4a6cdd708e253326b1092685ea1bf8268f81a94d24f35424cc49bbcdceeae8b7","src/_topic/mod.rs":"937d25b66169bbca7c97d9acf5eb4c9fd3bfdcdfcc8b39192d34e99eb1e42cae","src/_topic/nom.rs":"63141a80234da3a13ba074aedb3ed879588600705c236f30616c45d1731e6669","src/_topic/partial.rs":"2ee81a75c384c5e6fe1ebea3eb760be66142749785a5ae97f72e0299895dde2a","src/_topic/performance.rs":"8258b4ad39543d6d851319c55922b4af26711e75b7b171270c2c10337dfcd538","src/_topic/s_expression.rs":"ca745384c41df90add4da390fb5de3b21a3a49a8eeeb069796de2075d5ac86de","src/_topic/stream.rs":"f9e85b6bede8c514c0b84ac484ac615cab9c42a8f3f676ee014f90d79a2f5ada","src/_topic/why.rs":"40e5ebd322fddf7271283c01507a279e9563aaaa2eb6f3281eb0dccc22f43b4d","src/_tutorial/chapter_0.rs":"f64082bcd7e6e69d2bf0c4c7742502536930d6fcd542598d3b943c6f880ff34f","src/_tutorial/chapter_1.rs":"8a35197636d680b431c7f8ae4bd8c184b53e7b4d1ffddf0abb0e8f6971d1f63c","src/_tutorial/chapter_2.rs":"c63a0136334269561eaabfdce5269541b70d727423a266dd07cbe4b8dbb67307","src/_tutorial/chapter_3.rs":"440d0404d0f903d62ddae7578244d362ce8257f3d79b5fb19dafc607238f5ed5","src/_tutorial/chapter_4.rs":"394dc5eb022152a87edbb6cf3e6fa4b7b29a8b2bad918f69fa21622a8633ea15","src/_tutorial/chapter_5.rs":"022221e2cb9f7a3020b9ee13481d571be965a901c973b9a3c59507ba4891927d","src/_tutorial/chapter_6.rs":"37fc80b8747843b55d530b285f7f67319247678f32d380f7e472aac42ba1e453","src/_tutorial/chapter_7.rs":"b5ca138f12195417a70f5b068191075dce890bac02e417b43bf3ec624def37c5","src/_tutorial/chapter_8.rs":"360a550dd330991e219870da121ada9380a0bccea2c555017acee913bbb84dca","src/_tutorial/mod.rs":"29f78e7b383fa4aa90d72e6452b8d37cf34868dd2d2087f286577c5ce762bb7b","src/ascii/mod.rs":"094d12a192c1fafbf8d58d679f32e85159b36532de042a956a8b54b63d8f8b2d","src/ascii/tests.rs":"3734aa29e434b4fadbb123f26618a219a357a662c1d4d52a7c999c043ada09b8","src/binary/bits/mod.rs":"8807804337b8cb91bcc42d9d761fc79eac3301e33dc459c645134d04a01e72cf","src/binary/bits/tests.rs":"1ea9bf42b8ab77bea711ca6fc154751cf0379067b53c3a41d4557daebe8c8d20","src/binary/mod.rs":"793a504ef87d20f78b7d283f8bfd505bf9b6ea08e684d267126ee0fead93d2d7","src/binary/tests.rs":"49a32c8c0c03e789c4337943e047bf629f41d2d8910fdce85b272124db7b643a","src/combinator/branch.rs":"9ed597692c2006c86ae1297c1e4cbeaa5a5c08fd97c4f5362797c1dc96919f02","src/combinator/core.rs":"b5e2828f0345bd288c74f7fd1b1bde537d3b029ad1b718264a7f17347d85775d","src/combinator/debug/internals.rs":"65db58a13bd238acbcda2e9195d6086aed0abea46749df6755c48370d22fd02a","src/combinator/debug/mod.rs":"1ba4102cdf036e0964db3df2c21d4c27149f159e707006beac5a59b2e625bef7","src/combinator/mod.rs":"c2853feaa0a7c73b44cbf4aaee262043c3ed79d5537f29961798850e3d11d201","src/combinator/multi.rs":"e0cbe4f29c0fb08300a5bc6e5f1afc791ef4a7b9b14dc536ec04bdd50e26ea2b","src/combinator/parser.rs":"e27c71836dd0bbd653a9c319f6235e51afc9086695e40ae9c88eaabaa0acb17a","src/combinator/sequence.rs":"c7753b9de890a8fc2d01052aba2ac2d0c7e53f0541f8e25e0dc260ac2ef3e943","src/combinator/tests.rs":"f62f831596ceeb5bcdfaf2b8320b1a9d64cdfc3fc001ab88f15188c25920b2e5","src/error.rs":"0e413b68747fb8f6170c8a98a89756ebf4c0af5772ed52420c3e9d08b9813c7a","src/lib.rs":"5005ecd14a9d4779dc59f3dba4c4444524170042d7b0eafa7565786c702496ba","src/macros/dispatch.rs":"bbbe8f53ee232943ebd51c0388f41ff5dea79b71ad1e41c63b10e5d20fd7c1ad","src/macros/mod.rs":"1cc229c6cc6cb56e5d37ed10217d90e430b4ca0e006a0303060be5c2b4809f1e","src/macros/seq.rs":"c0dbc97dc5c23302ef36eb0bd6e6aba55627d4ef84d7a47f9dffdcd047b76a44","src/macros/test.rs":"9b480299987d3935b5ebd57f314e5b6413b46f8d0ce168980c938b452b899bfe","src/parser.rs":"2682d25a4479c02c91eadb3afb5e115befff67a0b24451f3c7afdb0f529f3e9c","src/stream/impls.rs":"74e80ad8fda33bd182e33f716e30ecb8f438454bc375f4000c5553fb93844e8c","src/stream/mod.rs":"39d79ab76d3d7aada84c651e16670df79433c837478252d77167646a46450d4f","src/stream/tests.rs":"29c34ca57b189162b303d3de50d5b36a35b7cc58522326fe4565d96b3fd25b6d","src/token/mod.rs":"206c3ddff0ef6fcd1f7bb8c2f08ea12f45370f5470ef6422c479801fb698e4f0","src/token/tests.rs":"189571d2a69f7879fbcf4dc8f9cee0a9af0ddf2c2fbd45823402cb9a6d1a8945","src/trace.rs":"bc3eea7f64cda9a893bb99863c088360f719bf00f95ee51b0538097071455326"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"c9f30f54d4f20588333a8fdb26279f82bf8459b85d68a30970ee0f47f4bf55cd","Android.bp":"9d54f882e444a0b3dadda5b5570c490275209b31597a7760883e74497026a5d4","Cargo.lock":"1c58647c7170d12483320b058f2555527a2b449e70979e2c942a7645e03a13d9","Cargo.toml":"34a2d60dffe4378d633995f0e70da9448cc5cfabd5adfdd28b30a72b3b4546f1","LICENSE":"c0eb8549d2cf81653902a4df95bcbf95657d17ac97ee17d4ac99409be3d02d09","LICENSE-MIT":"c0eb8549d2cf81653902a4df95bcbf95657d17ac97ee17d4ac99409be3d02d09","METADATA":"db85e0a97567123eddaf47161ad39242070b65f8aaa6c62e3273daa497d0aff9","MODULE_LICENSE_MIT":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"dab283945bcaeeab1ede8ee4521016c740bfa62b27385f4aedb72ebf38e3a68d","benches/contains_token.rs":"0f11db56776001e13ee9058ac7e2a31832d838bf83a3e0bc850d23bee2a5254c","benches/find_slice.rs":"8b0f978cde8992af1895bb0cf984e4c0b0dc6558f78080b167a58e6a40ced873","benches/iter.rs":"717e3fc61a25b4228ca9afb9e437492da4970e7803ea50865b860274b6180aca","benches/next_slice.rs":"5db6ccaad2bbf9f88c2bbf8573f35c42bbbbdc40813244a9a263fbcc0b6272e5","benches/number.rs":"48898f827b7545f9de081a94aa95715cc3dbae4f36345ff23d4b4f8a020607d3","cargo_embargo.json":"4322d11531d0decc7f497f1c135679c7f1573c2362137121d587bf6d49e21fcb","examples/arithmetic/bench.rs":"58d7c0e48a39d2f439adcbe75e5d537f036646835b712935d14478e2cab2f78a","examples/arithmetic/main.rs":"88eee2a4c18cf58b973bf1764d017d54028d59b8fa156f2b9862fc3f22bd65de","examples/arithmetic/parser.rs":"3d95be05390265e016e59fec0261547fa64146a5b3ab94c03b388eb650f35bc4","examples/arithmetic/parser_ast.rs":"b158850f51d3dd495ba7ccedeab1a39bbcfe86387c5c7ef9cfc2c870866c7178","examples/arithmetic/parser_lexer.rs":"cab8827532dfe7c72990f2817803569fe8ee4ccdad558db8ee13161493d0b113","examples/css/main.rs":"52d266f9f9e8c90e189febf21373ba4af9fb1f006e9671d346deb7403cd4652c","examples/css/parser.rs":"8f19e6aad0c141dd757988b029f67e5cc17a99bae331d931d85e20ad804c89b2","examples/custom_error.rs":"3a247addf688f4d6c85e2a8afb8d030012aeed9a8b85e58c0b6d8bd2d8ab946a","examples/http/bench.rs":"1e5d38c6411b3069bf7a998269fab94da79835c8c0fe9118b3dd271926603133","examples/http/main.rs":"cb229289c242a2c9393b574cdaeaa5f8b8f99d17ae6b7f2b9eed4e770e7004ee","examples/http/parser.rs":"be7dae5d458a3c10a5551d3264e6f685044c03b446263840f009570426510c8f","examples/http/parser_streaming.rs":"833ffc19cfb6494f4dfc8a466e6e1c8222cd5f13d1c15233c25ad229f8c2c04c","examples/ini/bench.rs":"0f563192241369dd34d2d8732c8bc27d802143345d96195782a5668f3fd55c80","examples/ini/main.rs":"975d1297560fd7d4e34d647e8badb31c11ec397c9ea637397fc8e05ded5553b3","examples/ini/parser.rs":"e676777f509fd719c0d3b5b477a417fec402a6bf420dc29ae611e7b378ef3dd7","examples/ini/parser_str.rs":"73147c6612e0aba1d0be5e4963933198205bdf0f5778451b0010582e40735d05","examples/iterator.rs":"0c78e9e20ff9351a68b3a73dcab248d471a64848f31a492a2a3b13d784bbb9de","examples/json/bench.rs":"69f7a196a986fc7489ae739566d8eea98ca525d1c1368920479d3f8831505b7e","examples/json/json.rs":"35d125cd92b9219401df343ac3d98f7e6fd516cbb3b5f09594aed73e7c3d50c9","examples/json/main.rs":"cf7038310b5fe17cb0552a4d94d9b3796eaf438769d85ec48e56a9fa61995f49","examples/json/parser.rs":"00ee33b2c3dd1345be1e6fb284dae27ba25ba7fb5f8109c0a3dc43036d3fe22a","examples/json/parser_dispatch.rs":"096176d51f2d85bff0b225472c72afe0b3195718ba441474f503fba01302c731","examples/json/parser_partial.rs":"a57051e2044e6a3c095d46932a093baacdeca26535d6c08c2c3488b91a33c44a","examples/json_iterator.rs":"8d3ef40d1dfc70cb8f35a968fce5c392c24241b1d91c36998d0ea303ab25e9cb","examples/ndjson/example.ndjson":"1c013270a2f5ee7d2df2ac6fa1b9d82af68afe954c7a0ed629c04e274d6f7bac","examples/ndjson/main.rs":"1675437d9b57073ff3b350b74f6d91db96147c1b72ed886a8a66d8d1221d1096","examples/ndjson/parser.rs":"067c7ff9c5d92bc2d167bdb02a25dadb680908496a610d3d487da361ada75dc3","examples/s_expression/main.rs":"2e365d7c5e5dbcddfcc4bdc0d73407ed041d270b524125c04e89ec9925c0274e","examples/s_expression/parser.rs":"ecb267b48407a35361281fa9f09d37331e5f6d0decaf69b9366502db64b7bbda","examples/string/main.rs":"a21024f6dbbf0c66189d265851009fe885d9f17ade62a66322baf003384ae305","examples/string/parser.rs":"46394e67d8bd3ab74fb1c80dfed1f4dd8381c097d89c53e8fdf492bd22542ab1","src/_topic/arithmetic.rs":"b79c44c429021dc2f4ce8bd2821b53eed1d65bfe47ba91bd036c17f748e4ceba","src/_topic/error.rs":"7e4a39dffaf5d2b9757d9d995ab3b200321cb3a5c175fd2a5f0880598c001a98","src/_topic/fromstr.rs":"aa7b4d94824efd91a6db87e5477f98f19ac20f931d65d09f064b167b424e8ed6","src/_topic/http.rs":"b4a0854c82f687cd4307b0ba1aba448a1a73cb06409a521b512735900a2a121d","src/_topic/ini.rs":"f7dc4e1c4d824597317e09a5a8737ac5b8578094d616d4cc9aa8367cbeb1747f","src/_topic/json.rs":"559d2a9d7b7461eaf92b6985a0e79f54c32a63c70ce2ea86a2528413e1f2ddd4","src/_topic/language.rs":"95e71875c6b923f4f8eb0debf77697f10084a33b51c857fd227ae826282ca4b8","src/_topic/mod.rs":"1eabc7567c01c58851cc1aa5c30a8896b5b6f87a96189cd9bc16e77401b2961e","src/_topic/nom.rs":"fecfc2cd238ed49cb91ce7744eb417b02df061c4ce0d7db7adebd09e2a2a86ee","src/_topic/partial.rs":"2ee81a75c384c5e6fe1ebea3eb760be66142749785a5ae97f72e0299895dde2a","src/_topic/performance.rs":"8258b4ad39543d6d851319c55922b4af26711e75b7b171270c2c10337dfcd538","src/_topic/s_expression.rs":"ca745384c41df90add4da390fb5de3b21a3a49a8eeeb069796de2075d5ac86de","src/_topic/stream.rs":"9eeb1f303f6b346f5ba0c64e4b120451c270ce1fb3409162e4d2afee986e2a22","src/_topic/why.rs":"06e0f617354d47f2fcc9912f390d3fe0d0f07e88d194bebb7551aff32d786fae","src/_tutorial/chapter_0.rs":"24e4fe727bf0d7a0e50fcd90019c3d11987737bd36d4abd79d399917096ba21a","src/_tutorial/chapter_1.rs":"4b7db4081993b1c231694a83676e714295af67369a99a0f38da72685973d73b1","src/_tutorial/chapter_2.rs":"1d9e8eab1c92a9eb868edb907b1c06ef2fb0145ed9a96d4a829dce548bf0b090","src/_tutorial/chapter_3.rs":"a83a624580d3f09beaea028524f11dbf65090ef87179e8496e07c2429c38fa56","src/_tutorial/chapter_4.rs":"03600ef6c02b080b31181c248e786e8b4a2cdf69862d0f4f8ba11622cfe7b2e7","src/_tutorial/chapter_5.rs":"d42dfc2fb81fbf2bb6bdc38088d93e8c1874172a1358ce23e0e0ce40ccd6dc43","src/_tutorial/chapter_6.rs":"1fe7efd324918c8d91bdec3b4a56d44cc82815d04c75b25bb2b32e4f21f8f2fa","src/_tutorial/chapter_7.rs":"bcb56daa6d06caf0e5de3b471396724731aa595b32898c5b70da030601d35595","src/_tutorial/chapter_8.rs":"ca042e342d96d2a33b9211b975c4b35fde9bd05ae7f61567f28ccb1d4a9407d8","src/_tutorial/mod.rs":"29f78e7b383fa4aa90d72e6452b8d37cf34868dd2d2087f286577c5ce762bb7b","src/ascii/mod.rs":"27c72fdc650b4bd72d7d17799a05efb2c99e678f71e14aa6473abad22ee126cc","src/ascii/tests.rs":"8d5094fbbc19bd8760e886efd9c99d0110368d7a9f5af5d18d06212f785af260","src/binary/bits/mod.rs":"3a9971974c8a66099cb75e5c95140494fa18fa904a2737ccad46ed74f5d9f281","src/binary/bits/tests.rs":"688bd31747f074e974f494bd27103997d790336d34a75e4836aaf011ae7720ac","src/binary/mod.rs":"e909d0778cf8ffd4a64c83b772844f2c298db4d8e25358a12553563a21227959","src/binary/tests.rs":"1a7d8362d43c3cad6eadd0e46280a35f146508f2839967b41d744ca7df913940","src/combinator/branch.rs":"fb3241e3a1403e3f3aecc4ad227ff51cf0c37a3fe54789a84d162782ae119e9a","src/combinator/core.rs":"ad11bf23a0d8d54543aeba8e06eae06a31450b4e5cfb126b600b214b264d2e18","src/combinator/debug/internals.rs":"f68abbdee3651c17971915348c4812834b7c6c38f58f5bb1da8d5b2cd443e337","src/combinator/debug/mod.rs":"1c6e836a80a2c966e71d4f292f021ae10f6ded08c926c76da2fd0e549500ea56","src/combinator/impls.rs":"8dfaa0b5f4a59e8b21651eef38470c1d6ba745e5e32d0873dcbff7da648735c1","src/combinator/mod.rs":"cf29982a9ce75b8272bb687360288649ff44219123e592b7b8e1e8cece42dd65","src/combinator/multi.rs":"1e791ee30315c9400f9ebae7799fc753282752f9ff516651d0b1302379a769f4","src/combinator/sequence.rs":"3bf9f13a3757f11bef108d6667f89b0221b3d51151d0f2319916b3fdf1f10458","src/combinator/tests.rs":"770569359f5a35eff92309739555ce522942661da0391fa72ad2253de129418b","src/error.rs":"fb24df66ae692111187cbf75c27100dfd2ef359b2a465810249b264d372b2b6c","src/lib.rs":"de34b284156c358021a13bed6edd58a138178b955553b0beb8918e86a11231dc","src/macros/dispatch.rs":"bbbe8f53ee232943ebd51c0388f41ff5dea79b71ad1e41c63b10e5d20fd7c1ad","src/macros/mod.rs":"06eed9d8626d444dbfcff753f83836da4474a9b145fea3992d89877d7f4023e6","src/macros/seq.rs":"e241cd8c2afd9033e2362deb4cfa9da3b88d4b96c4e6b92a0addef2b14b8bbb3","src/macros/tests.rs":"8bd38c7805192e2df69c4972ab8e43f33c6d1f90828c4bb9ce30f0924e9c3ae3","src/parser.rs":"446232c519f2ccd53a22767332883a2bb4439234728a1f419d19a80dc4a3827f","src/stream/impls.rs":"5920f82ab5e4c81e66ad6f30830b9d5adf22178e6a0441eeee92a4dc742bbad3","src/stream/mod.rs":"511b36ff2e27f2859428cbcaa56a65507964ffb097d16a6e3f796e1a1ffba42d","src/stream/tests.rs":"7d40f65e6f32f8fd23b5043b02e2a248c8fff211b71cc32aa8c2d7f9ae40d1e9","src/token/mod.rs":"c5461997b4b750125284a7f23fd5318cdb260bf8c3caa87c1204d12043a090d1","src/token/tests.rs":"ab4892648202709cf7e1a3999563656460efa43c853f64a80cfdc294b7b8bfff"}}
\ No newline at end of file
diff --git a/crates/winnow/.cargo-checksum.json b/crates/winnow/.cargo-checksum.json
index c82a3d6..903a57e 100644
--- a/crates/winnow/.cargo-checksum.json
+++ b/crates/winnow/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.lock":"db694e7e6a1d861cf51cc0db96f47cd04136035be41b456a871d6be38df7909a","Cargo.toml":"282f87b5600ef0f2476bba16911877bce142c1109930a5b297d702ab0ee72f83","LICENSE-MIT":"cb5aedb296c5246d1f22e9099f925a65146f9f0d6b4eebba97fd27a6cdbbab2d","README.md":"0b7e4cad5ef0cbb47dc1c1c34ff3b5d090aaba2cc3b3c5401539e5c4a22b7938","benches/contains_token.rs":"20e765de27f0ad9bf549e250bdb67d73714d23bc10bb67be240a258dcf8b6ed7","benches/find_slice.rs":"4853142ad538403b17c5f2d486b958a11628039b5366925927e61743ea462c48","benches/iter.rs":"bede3f156b87ffb8b901b092bcbb2717b7adf9a461a2fecdfad86025aad3e587","benches/next_slice.rs":"7cb1ea5e7122676532ecfbc53e0de78417bf96546c63a170370db8a99c90620b","benches/number.rs":"f4b54895ed523c462ce327a5205b0b99591eb7056fe9de9abe3f809eff287386","examples/arithmetic/bench.rs":"a162e4f91ce2ed19b5b53f79d77c92eb52278f0788da9af8fee3ee62cebfe0a9","examples/arithmetic/main.rs":"b7f2090beefa76ca324d23bc62e06ef97df8c7d02934998f4ddce1ac45e0b852","examples/arithmetic/parser.rs":"318f804e7324b762a7f84f4202537a21c2a299111b42e559d682a6d01857119a","examples/arithmetic/parser_ast.rs":"93f4b8a361bf37f20cba941a9bee878890e86eee0eb8256162f52910a8c43fe2","examples/arithmetic/parser_lexer.rs":"ecdbe258e9f88d5f25f6a57da3de5f4426b01f86f7ba20d2fd4e35a75481d2c8","examples/css/main.rs":"3127f259af57aaaa8363bfa6e0a64c9719173cc1dcc5a293771a2c8a7f31982e","examples/css/parser.rs":"e81d89b876288637d8480fe981ddacdf2faa7f54da051801ee308db39834497d","examples/custom_error.rs":"5ebf88007ed2ce998fdb16cd4326b1573e83f0c420e2f6561c10b6dd8ebf69a2","examples/http/bench.rs":"b6d3e9e5833b53ac58f8336617cc42a2f3f4c5e5ce88feb23184fcdbb36843f6","examples/http/main.rs":"95d3c73953cd76ea462d886ade2ab34e987239bae3abb0349c434fb01b80467d","examples/http/parser.rs":"21ebf9083fd5ced93ba74865bab80c76ba6ec0dcdf9f6fcc95acb7c0c90c2a73","examples/http/parser_streaming.rs":"468c2896d03d0d35bfe0ed750c13643e9a13d2dc4a651c0d9ffc024aba7fada7","examples/ini/bench.rs":"c8fd1aed25ce2878c0f440d765779d7877e83ca202cf071f8f4c57566596bf89","examples/ini/main.rs":"d771aa8aae11e53f4f5453ac39e10ae3a1de210408dfd31ca7d1ce58d14c3fb9","examples/ini/parser.rs":"c554286e8f2077f999d607902e7a63ce3945939118bccf830a68b22bfe24c775","examples/ini/parser_str.rs":"d55687d113440e40ce6df400a49331183feac3e904844af57b956caaa567fcec","examples/iterator.rs":"21fb0480749116407689162f0d3e9957e3e7b6863f9dda08ee01af675409b06e","examples/json/bench.rs":"9479248ccef46e0741e0eebf9fb3b307b343ba665925481adc4554d8e65f9d39","examples/json/json.rs":"48cf6c114098113c9767bebe430682f55f7c1a227e6486131ca58c00770711f2","examples/json/main.rs":"7ab1f6eefd4eb61153cf05991e593fd10827ac09f66af45d3019f94c39c5b84e","examples/json/parser.rs":"b7de8bfed29aa5c1da9f483d601dd2bb4129b8b2783bd0d339e356f34b265254","examples/json/parser_dispatch.rs":"148bfb3a1c73181b935dd4ad12ff1b79ce375f908377b93bdbfe0c7339d4e513","examples/json/parser_partial.rs":"d39c6749038031a363a79d9ee87fe9ea8c2bf21a321884845f37a1f63cac2905","examples/json_iterator.rs":"2a8a842b43fdea12a6b5c4927cede23cbd0eb96bb1cbf42a794103fc9be9ff3a","examples/ndjson/example.ndjson":"c44c130731008bca76181348d40f46b183577949b5a4d229e0e1a56e1e405e5d","examples/ndjson/main.rs":"dc92dd5d76b5d43edf947b6455d552f0212e46d59c926d95f97f5d159b14e361","examples/ndjson/parser.rs":"948a876edc6fbac22920c40c40c319fc48806544b7b7bc6d17109513d87e8179","examples/s_expression/main.rs":"5a2de0b477807c7d6ab8bb72579f03692e8f5ea89d5b6f3a8342cfdbe4d84b96","examples/s_expression/parser.rs":"2a468bf61e4aed9aa5c556fb19c479060d121f60671865324675ab9fd92f0ddd","examples/string/main.rs":"e6362f957d532d41bcd96476e55ac4c253eb04fa25ee88b48d872661a46a5bb5","examples/string/parser.rs":"7ed4bb82c4ec7f4b5a88b1ef127d7b6e59f5ff4105cc8d6b4590cf8d242f0799","src/_topic/arithmetic.rs":"28d61dd492b308209d534c54813ea48ac1862a39196527042180de7718f915d0","src/_topic/error.rs":"0d5e9631ff497d52e9da21958cae1b12e0e5d7293b0e650c5321a4d2a2bdcbcd","src/_topic/fromstr.rs":"01abdab296cd30067ae5f508b2b21ebe97c0571ace58e7817876eb2110e8d23a","src/_topic/http.rs":"19b9ec78a031fe5b3086fb34d04d6c13308c50b6e7bfe30229f5b682d3605ac8","src/_topic/ini.rs":"b2b04d48eac3158f4e26ee9dce748a699d02acaa0b12ae8d54421cad0fdc4ad7","src/_topic/json.rs":"bde39ee6b1c5fab2d2da480822b737674aed877f92c3f748d6932bec313b5661","src/_topic/language.rs":"61fc2e5bea30ded24fad43aaa6a7d5de940e6236f95c54093490be79514ff4ed","src/_topic/mod.rs":"c52d1cf092892d2b7548316a7ce77e2fabca1bd43e2bebc549f2a3f5d2fe9e9f","src/_topic/nom.rs":"9e7c4a900a7ad1bd417d9a72f2a5122a75707c5d53a2d39845b350d5aab362ca","src/_topic/partial.rs":"4777cedfbe8437a547114da346f6cd350d8e77ae62e2ed32d50636e9acbf876e","src/_topic/performance.rs":"115b636769c307bd80ecc5b89ffad47877352bc93ef47bf664093d3d7a2562cc","src/_topic/s_expression.rs":"6ca9a22a5c3344e120421f2ab353a1e822777aebfa1faa0acffc9632f7668cb2","src/_topic/stream.rs":"acc1e8942e27fec3911c562934be6bf9b096d436593af50a4418d9e0fe607535","src/_topic/why.rs":"097dbb252ed0c67e0d50157e1826286997450a9c413cf40697c0918e0dfecba1","src/_tutorial/chapter_0.rs":"c0512cb9857edb9df0ac1d327fccb1e950019b9a417959c802714039038b8bce","src/_tutorial/chapter_1.rs":"743847b36faab89235f7e4d36076b5090d1dff7c0f5f38f9c3387aaff80bbec2","src/_tutorial/chapter_2.rs":"3671bfd14e7a9c6c630daac8bc2c8128938fd0f830a5524c33a800d06d4cd205","src/_tutorial/chapter_3.rs":"b5dfcc806552ee958ef27558d377d6abee1c1437a5829941c51f475f389e2aa4","src/_tutorial/chapter_4.rs":"4390d7ec2a67a726bd8df7f62ebd3fc26df926e79c014b8a2e623ec3e4c1ca25","src/_tutorial/chapter_5.rs":"25656769ea285f9d6d3780294a24491b27918cecd754b430220b56ac2047a10c","src/_tutorial/chapter_6.rs":"51fae6d7479288f518d04d43b3e987aa0f26f83ff0620ca4bdcfe7ec92e2e0cb","src/_tutorial/chapter_7.rs":"6ffab9e8a2a7ef6c38dfb097a1f8ab6bffe86545511259a00e81e5b6e4367650","src/_tutorial/chapter_8.rs":"ddd6ec7cdd2d888a2361c4715221be2fdd24475d7be2953474fdd2a01ce6234a","src/_tutorial/mod.rs":"afba818d7d1c8309bee5399dc8f695d93b08e93f406734d41e7e66c019b43c53","src/ascii/mod.rs":"d607899a8399e769659d54dbee12e783239d7574224f507d09fbd0baef7294c2","src/ascii/tests.rs":"fc44d6120c7e6ca4f70684f668e195884ff1193e6924b99093cf411ad90f4a8b","src/binary/bits/mod.rs":"c0ba2cafd89f11d3f70c036b713e7792d7b55aa61cf91f12dc1a9d88766b660a","src/binary/bits/tests.rs":"1ce1707a8ab2300d601f67fb3cb9c4b8ba7528564477e65ff25d8542c3e0f6e7","src/binary/mod.rs":"79bf5f8f49ca21205afa2666762b68ec5769283f5503e86eacf6abe88b639e63","src/binary/tests.rs":"107a6a36115f0a52b5adf9e5e3bd2d242fc43888e916d3acca772c49563c545b","src/combinator/branch.rs":"243f68835c1ebd8407658f823093fd86f2e02d2dd79dcbde576b87c78e835689","src/combinator/core.rs":"a7c773c79001e061742327b4fa99082e4cb023e26defc5f23fc9f31fc83abc1f","src/combinator/debug/internals.rs":"3480e946ba22390ac84046487728fd89d78cb3bc5547305162ed925e2437802f","src/combinator/debug/mod.rs":"2b876e31880f3a1ff84585a5d31458f5c0e135a9d6176cb36859e365cbbe8baf","src/combinator/mod.rs":"b6d97df5256857b95d9d683682efc5b6078b43a3abd0a51bd55df6da93e67f9d","src/combinator/multi.rs":"ba423a6b199ddb550f9f2c63700a0c5ef16c5652863a1ad67c47210f63efe837","src/combinator/parser.rs":"a3ee56f3d12b6d1a5c74e590b646bde5aacf653d64fbb9a983ec2402b7af80ab","src/combinator/sequence.rs":"9ecf8e294fea355d00b4cc8d8a82c56b0565bc647d4f294a8c73c1d85d7dd355","src/combinator/tests.rs":"93848556fb5461e0bbbd9926d30204ef6980c88c0b36351048a8f140c0de2124","src/error.rs":"61d03a606b543e0e22eeefc4ee15abd7745fff6d089d904702692206aba4cefa","src/lib.rs":"a967f08aea09bfc95eb53112fd4718baa39819f9c35f9098bf29c5e0a4445a30","src/macros/dispatch.rs":"6a82b6f3472a58d114e2b40443d7177d326f9b9b7b25d1370b4867838e0e1e80","src/macros/mod.rs":"9faebae01ea216cda6160cc306ca4a5f531344aa65a628973c0825d0f53b3fb3","src/macros/seq.rs":"c64d7354d27c21b37901887f0bd3fb1c720adbe6c677a02880636d0ba9fc9491","src/macros/test.rs":"7b1df320d703fdedf90204d094c209cb69a3022b2cefb27803fb73031596b87b","src/parser.rs":"a26ffd1c9cfcb518b3224165dea96b6fe66c6bfad9a96b2b20745134d7f72419","src/stream/impls.rs":"139025ed7c350c86e14b3a23019b1e5569cfee98c5f3e07d5a7dc69f411a8730","src/stream/mod.rs":"db11794ada41fc3145691eab621fdb07096229bfe951f5c8e682b99cec4f7b11","src/stream/tests.rs":"399d884c7b1baf256f667f8dd25218fa473bd30a3b293625f756a9accd5b1f2d","src/token/mod.rs":"fc109bbd13e3531376c8a2cc3be6dd0c93b2fbf30113df958b6d862c432faa19","src/token/tests.rs":"74b54c5b631a29377c0af15329c822fed9752031d04531444d4c2b23367e3a5c","src/trace.rs":"6a1f066c7a3e4d93b6fdb0fa53c08e744b19173bb669f389933786908a88a5e0"},"package":"a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5"}
\ No newline at end of file
+{"files":{"Cargo.lock":"db0b0c8f9fe2fc0bfabe9268ec881fe129a6e56fdd7d8749fe5b6f183a79238e","Cargo.toml":"375763b9a87681acabc281125d064c7ef7a49bc67e2502531f4613bd758c7735","LICENSE-MIT":"cb5aedb296c5246d1f22e9099f925a65146f9f0d6b4eebba97fd27a6cdbbab2d","README.md":"0b7e4cad5ef0cbb47dc1c1c34ff3b5d090aaba2cc3b3c5401539e5c4a22b7938","benches/contains_token.rs":"20e765de27f0ad9bf549e250bdb67d73714d23bc10bb67be240a258dcf8b6ed7","benches/find_slice.rs":"4853142ad538403b17c5f2d486b958a11628039b5366925927e61743ea462c48","benches/iter.rs":"6a6c316e0bc28e31ac861d5f33c5d9976a3fa8e0221828dbb7ba197ba27685e9","benches/next_slice.rs":"78968522a84d357d3f7d5962749bb37766b1235a12cadfce7f015759c91a4688","benches/number.rs":"f4b54895ed523c462ce327a5205b0b99591eb7056fe9de9abe3f809eff287386","examples/arithmetic/bench.rs":"a162e4f91ce2ed19b5b53f79d77c92eb52278f0788da9af8fee3ee62cebfe0a9","examples/arithmetic/main.rs":"66212042c1810c3fe06e6b6b225ecde4a66f4cc378ef1ea4c7533e1420d7e257","examples/arithmetic/parser.rs":"9630411e61c96df6eb86641cf4c1c381c43a920779badaae2ca6aed49eaf826c","examples/arithmetic/parser_ast.rs":"853e79366d0ed7995465c7657d335c386ddf1171e8417de674d8d2ad6f5c7c20","examples/arithmetic/parser_lexer.rs":"e7d746867c6b0fb3d9c70c6e2778cbabebb15d052926b3989f35ada82bdd2b15","examples/css/main.rs":"06647d68a57f8ef47198bda81dd88b2e123e70c7e1a79a0109a759ead1212904","examples/css/parser.rs":"ee0c553b9e68729a311f00cb49366cda96731d5d69d4d7454160371e01ab30e4","examples/custom_error.rs":"b6fca4f4db0c61f9d941e36aa53d4035d11b5e47d2e6e12d4639dcdd9a59fef3","examples/http/bench.rs":"b6d3e9e5833b53ac58f8336617cc42a2f3f4c5e5ce88feb23184fcdbb36843f6","examples/http/main.rs":"ae6fbf6b7083e73b3bd29cb63094f0aace6f7834c90504fa1f3abdca87d81826","examples/http/parser.rs":"3ece9ff3349cfb3c89c0bb7ddc915ce679139cece3cc0ff929fbd41b05d30c5e","examples/http/parser_streaming.rs":"979df0482e516037128b7b02a6b4c08ac3b36603955289bba772d011019d543e","examples/ini/bench.rs":"430ef9276fb284f93e50ba87c6b1a3e4cfd2b4e6cc5f4d76bbc0d40a26112299","examples/ini/main.rs":"b3b6d508a6e8fa99e34fb400a625d346da6eeeea909ad5e365636acd1a8a354f","examples/ini/parser.rs":"78e8576cd53ef1f7d37169e4e5558aeaad7701841978cdf19f2e0e79d86a9f06","examples/ini/parser_str.rs":"61497ca84ab2e4e945bd249d8dc67816fdb646c06b8c2456b74ef88933d96487","examples/iterator.rs":"7794f6fa5306e53024b9b1686f67df355efb9404cee2b2e7de3b6af5d0d3bfcc","examples/json/bench.rs":"01061b696d94952e0f9cf2e7bb4bb2cae6cf1069e36d9fadfed4379e8502762f","examples/json/json.rs":"fd9fa5e4b76ba1f7d9180ae8127751d2c665892ad6d1c95f4eba16ceca6ff576","examples/json/main.rs":"d4857f9e58d0795ea6c9f33b271eca1f7b2aac13102c9eb723b630fa99fc2adb","examples/json/parser.rs":"a971afb90ca2279c62c8013ae3e1b84e7c73c668cb05288686d992e39a06d15b","examples/json/parser_dispatch.rs":"16b0199212e774cfd8cc838096fd75c511991eacdb8804b88dd8a0be1c6210db","examples/json/parser_partial.rs":"cf72cc355acb41a66d07e6fe6c8957e745761c854f39055aba4cd667425546ea","examples/json_iterator.rs":"619720c801603ae6d8285b3aa682198c7126575ee143c3b59e0873208a6fddf3","examples/ndjson/example.ndjson":"c44c130731008bca76181348d40f46b183577949b5a4d229e0e1a56e1e405e5d","examples/ndjson/main.rs":"aaffe73971ea5e3f5aff7e40a5d091e77a95e5e5be997f8f840b9dc934c3b45d","examples/ndjson/parser.rs":"ca03480022822a7a76f06daab99880e5a05651e70824810a9a51fb4be08ccf32","examples/s_expression/main.rs":"5a2de0b477807c7d6ab8bb72579f03692e8f5ea89d5b6f3a8342cfdbe4d84b96","examples/s_expression/parser.rs":"9fbd4d6e836e4c4d8724a39592579243f805bfddd72883cca91ec9c51eedf1ca","examples/string/main.rs":"5537748a97de502ba5965f70fc6b9dcb66a4c051e88dbe5dfe604ce1bc0cae35","examples/string/parser.rs":"735a7cd220f058d398feb826b1ff9a7301bfded0fc7800f13f721e031a1725f8","src/_topic/arithmetic.rs":"28d61dd492b308209d534c54813ea48ac1862a39196527042180de7718f915d0","src/_topic/error.rs":"98fad96735246bd12757c596926f65757b2942564a8cccc159f74715631defc7","src/_topic/fromstr.rs":"01abdab296cd30067ae5f508b2b21ebe97c0571ace58e7817876eb2110e8d23a","src/_topic/http.rs":"19b9ec78a031fe5b3086fb34d04d6c13308c50b6e7bfe30229f5b682d3605ac8","src/_topic/ini.rs":"b2b04d48eac3158f4e26ee9dce748a699d02acaa0b12ae8d54421cad0fdc4ad7","src/_topic/json.rs":"bde39ee6b1c5fab2d2da480822b737674aed877f92c3f748d6932bec313b5661","src/_topic/language.rs":"5853c47cb42c58d8554afffd970c7810d2a84ba774942764ff5520f997601538","src/_topic/mod.rs":"2e7ee6ee1e2b39874ae37120ee40a4a55baa0536701c1ee62777bbf5b86a4ac6","src/_topic/nom.rs":"94b57630f91f716f1583d1590abe03360d11fc7cbaac03e6c7154ef404160cb0","src/_topic/partial.rs":"4777cedfbe8437a547114da346f6cd350d8e77ae62e2ed32d50636e9acbf876e","src/_topic/performance.rs":"115b636769c307bd80ecc5b89ffad47877352bc93ef47bf664093d3d7a2562cc","src/_topic/s_expression.rs":"6ca9a22a5c3344e120421f2ab353a1e822777aebfa1faa0acffc9632f7668cb2","src/_topic/stream.rs":"64aa893b39d277b03fcc30ec25365db9d989cdfe042b10bc17ab59b8ffe4ec63","src/_topic/why.rs":"645f8023ca7b33a32fd2265e5ec87286ad43acc4b8318a4539eb8c9c89f1267f","src/_tutorial/chapter_0.rs":"dbda2c50ccfc3bc04c798a57d7019ec93a99954327a733a7f48449da8c2ceba5","src/_tutorial/chapter_1.rs":"d96b372e2542939972ef22e852e2f413d7322792ce569cc59e4167ffcf8177ae","src/_tutorial/chapter_2.rs":"149465c97a0b65ed90534455d3c4e6256a80610020aad7519cdf8df1ff7f8d1d","src/_tutorial/chapter_3.rs":"f644c0fa752de95cc9b936a62737c89e59e00ad9a21fe93c75d52b599e16e020","src/_tutorial/chapter_4.rs":"9c94695afef4b24c066a68baa09f4bb7b3421c77c59bdf090f38b8b1fad82b7e","src/_tutorial/chapter_5.rs":"7321beb11338245a5bc0b321518fc7454c6b209c3f516c289dacd0eaa66362b7","src/_tutorial/chapter_6.rs":"4243644675ba7295bd4e25f4afdc45c06c1f2c04c4d0572089dc561a79811a12","src/_tutorial/chapter_7.rs":"218ed0f56eeec0b7bdd060fbee4382df41932fe6b17f653e7ffc34879d7b6d25","src/_tutorial/chapter_8.rs":"efa2d44ec3d35316288e33a9f5003f03b979756ecae928a16ff9e71c84092a9a","src/_tutorial/mod.rs":"afba818d7d1c8309bee5399dc8f695d93b08e93f406734d41e7e66c019b43c53","src/ascii/mod.rs":"e9b2c98984b00d89a51e52b76c9e99852cb50a5a2c5571eb0b3c4a61c2d05406","src/ascii/tests.rs":"f5b364c0d37a5c47214119877e90b7cae6e66e856a5ca907376ce3f4a8dc02a9","src/binary/bits/mod.rs":"2d6cfbc162fd739ecb7c084382c3a87d8a61d856a1bd26183116576cb8ad1544","src/binary/bits/tests.rs":"cf2da97a6e84a5391ec20cc9a50093431cce160308703b4db1daec8bd29df199","src/binary/mod.rs":"1720840f4818d6bc2a5e9c0fcef97a57cf9a88b2cc4654e8b4ddd7865858d012","src/binary/tests.rs":"4a082c68f2356da97a699246f586e9c5ab584a3c600d9d68883de1450fde2f3b","src/combinator/branch.rs":"0a154e7814a2eb3f0cbc53f4646c54811a5271705922ed63fd53ccbd21ded86a","src/combinator/core.rs":"586394dee9e4c02a09f1bdb52d166901110500325580edfc8be515a84c97f2c1","src/combinator/debug/internals.rs":"3573d767ffdd93398a6f13799584f1ce9d3ef2854f9cb761c0318acfeb16e6e4","src/combinator/debug/mod.rs":"ac7c9035154fb15a4c66321286f066fc6a77cf9de954066bfc274cfe7eb76054","src/combinator/impls.rs":"932d21cd65b15db8612f4b3e9442797f0bdd3a414f23212b1fc542c8aa1c5646","src/combinator/mod.rs":"c30801cf4fe8af1770f71e96a2ed98d3625d9822c1da932e2cd315e034add2bc","src/combinator/multi.rs":"40e9490b892df3bfa3278b1455735f8e8a8ffe34aec54c6f9cc2e51f37af18b2","src/combinator/sequence.rs":"cc1dd9b562dcdb13da6842596852990d0ec16dc203ba65e1430ec3f38ad014b3","src/combinator/tests.rs":"147e334d25e26bcc222eb1d993e030dab8afb1771b90bb5830b57b2cef8cf472","src/error.rs":"88b068733c62417e12673df380129f4f4b310341a0a1e0d009c9c76d299977d1","src/lib.rs":"382043729e04b93b97467c04d0f2ebe262a92513877aa5081d72b4b8fe8b6875","src/macros/dispatch.rs":"6a82b6f3472a58d114e2b40443d7177d326f9b9b7b25d1370b4867838e0e1e80","src/macros/mod.rs":"2946d54397a9cdab1e584892e3b78271f63dd1076c19bec81b5ccbc55b32c190","src/macros/seq.rs":"ce6c5b19ac595e7cc7c828290b9217a4a620807a84ae5c0678e41f28549aed39","src/macros/tests.rs":"af979e4b176cb04971d1adf9d02e84dcadd19589632365eb18a27e6c48c5c8d1","src/parser.rs":"88da095a92e8e9aa42aedca7061ccf27f61953ac4f4ccbdd79213b6cdc2092d8","src/stream/impls.rs":"fd3ad050853d5e36528276ac223aa5f953d686fd724703a90633e141365ca876","src/stream/mod.rs":"a874fa56952cf74a4b7ee2f04af00c2d2d16c0cc260aa50506dae823f0527937","src/stream/tests.rs":"ff6b23e887340b5fc9e6d0bbe8c8933dc9af60d159df9167cc606c613724ff34","src/token/mod.rs":"322a95e765eb22ffa148e56a3390028acad52dd663700e8d9c0b501edb53b35d","src/token/tests.rs":"a11bbb20f8d628ca69f736c2cf11a70a2e106602e15fd40f11e9913f10aa4bc1"},"package":"c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"}
\ No newline at end of file
diff --git a/crates/winnow/Android.bp b/crates/winnow/Android.bp
index 79f5412..ad0ee29 100644
--- a/crates/winnow/Android.bp
+++ b/crates/winnow/Android.bp
@@ -17,7 +17,7 @@
     name: "libwinnow",
     crate_name: "winnow",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.5.37",
+    cargo_pkg_version: "0.6.24",
     crate_root: "src/lib.rs",
     edition: "2021",
     features: [
diff --git a/crates/winnow/Cargo.lock b/crates/winnow/Cargo.lock
index 9dcaa1c..70984ef 100644
--- a/crates/winnow/Cargo.lock
+++ b/crates/winnow/Cargo.lock
@@ -9,6 +9,16 @@
 checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
 
 [[package]]
+name = "annotate-snippets"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "086b0afab3b349e5691143adbfb26983821e3eec4ba4c51957104d372c2e1b7d"
+dependencies = [
+ "anstyle",
+ "unicode-width",
+]
+
+[[package]]
 name = "anstream"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -17,17 +27,32 @@
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
- "anstyle-wincon",
+ "anstyle-wincon 1.0.1",
  "colorchoice",
  "is-terminal",
  "utf8parse",
 ]
 
 [[package]]
-name = "anstyle"
-version = "1.0.1"
+name = "anstream"
+version = "0.6.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon 3.0.3",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
 
 [[package]]
 name = "anstyle-parse"
@@ -58,6 +83,22 @@
 ]
 
 [[package]]
+name = "anstyle-wincon"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
 name = "atty"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -75,6 +116,17 @@
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
+name = "automod"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edf3ee19dbc0a46d740f6f0926bde8c50f02bdbc7b536842da28f6ac56513a8b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
+[[package]]
 name = "bit-set"
 version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -491,6 +543,15 @@
 ]
 
 [[package]]
+name = "is_terminal_polyfill"
+version = "1.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b52b2de84ed0341893ce61ca1af04fa54eea0a764ecc38c6855cc5db84dc1927"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
 name = "itertools"
 version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -720,9 +781,9 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.63"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
 dependencies = [
  "unicode-ident",
 ]
@@ -764,9 +825,9 @@
 
 [[package]]
 name = "quote"
-version = "1.0.28"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
@@ -945,7 +1006,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.60",
 ]
 
 [[package]]
@@ -978,11 +1039,11 @@
 
 [[package]]
 name = "snapbox"
-version = "0.4.11"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835"
+checksum = "9ad8c7be18cc9ec7f4d7948ad6b9df0e04fc649663e3c0ed59f304ed17ca69e9"
 dependencies = [
- "anstream",
+ "anstream 0.6.14",
  "anstyle",
  "escargot",
  "normalize-line-endings",
@@ -992,11 +1053,11 @@
 
 [[package]]
 name = "snapbox-macros"
-version = "0.3.4"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31"
+checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d"
 dependencies = [
- "anstream",
+ "anstream 0.6.14",
 ]
 
 [[package]]
@@ -1012,9 +1073,9 @@
 
 [[package]]
 name = "syn"
-version = "2.0.22"
+version = "2.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1063,12 +1124,12 @@
 
 [[package]]
 name = "terminal_size"
-version = "0.2.6"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
 dependencies = [
- "rustix 0.37.20",
- "windows-sys 0.48.0",
+ "rustix 0.38.6",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1127,9 +1188,9 @@
 
 [[package]]
 name = "unicode-width"
-version = "0.1.10"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
 
 [[package]]
 name = "utf8parse"
@@ -1189,7 +1250,7 @@
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.60",
  "wasm-bindgen-shared",
 ]
 
@@ -1211,7 +1272,7 @@
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.22",
+ "syn 2.0.60",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -1282,6 +1343,24 @@
 ]
 
 [[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
 name = "windows-targets"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1312,6 +1391,22 @@
 ]
 
 [[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1324,6 +1419,12 @@
 checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
 
 [[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
 name = "windows_aarch64_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1336,6 +1437,12 @@
 checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
 
 [[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
 name = "windows_i686_gnu"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1348,6 +1455,18 @@
 checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
 
 [[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
 name = "windows_i686_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1360,6 +1479,12 @@
 checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
 
 [[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
 name = "windows_x86_64_gnu"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1372,6 +1497,12 @@
 checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
 
 [[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1384,6 +1515,12 @@
 checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
 
 [[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
 name = "windows_x86_64_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1396,15 +1533,23 @@
 checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
 
 [[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
 name = "winnow"
-version = "0.5.37"
+version = "0.6.24"
 dependencies = [
- "anstream",
+ "annotate-snippets",
+ "anstream 0.3.2",
  "anstyle",
+ "anyhow",
+ "automod",
  "circular",
  "criterion",
  "doc-comment",
- "escargot",
  "is-terminal",
  "lexopt",
  "memchr",
diff --git a/crates/winnow/Cargo.toml b/crates/winnow/Cargo.toml
index 61cf661..7e93658 100644
--- a/crates/winnow/Cargo.toml
+++ b/crates/winnow/Cargo.toml
@@ -11,9 +11,10 @@
 
 [package]
 edition = "2021"
-rust-version = "1.64.0"
+rust-version = "1.65.0"
 name = "winnow"
-version = "0.5.37"
+version = "0.6.24"
+build = false
 include = [
     "build.rs",
     "src/**/*",
@@ -24,7 +25,11 @@
     "benches/**/*",
     "examples/**/*",
 ]
+autolib = false
+autobins = false
 autoexamples = false
+autotests = false
+autobenches = false
 description = "A byte-oriented, zero-copy, parser combinators library"
 readme = "README.md"
 keywords = [
@@ -39,14 +44,11 @@
 repository = "https://github.com/winnow-rs/winnow"
 
 [package.metadata.docs.rs]
-cargo-args = [
-    "-Zunstable-options",
-    "-Zrustdoc-scrape-examples",
-]
 features = ["unstable-doc"]
 rustdoc-args = [
     "--cfg",
     "docsrs",
+    "--generate-link-to-definition",
 ]
 
 [[package.metadata.release.pre-release-replacements]]
@@ -90,58 +92,91 @@
 replace = "blob/v{{version}}/CHANGELOG.md"
 search = 'blob/v.+\..+\..+/CHANGELOG.md'
 
-[profile.bench]
-lto = true
-codegen-units = 1
-debug = 2
+[features]
+alloc = []
+debug = [
+    "std",
+    "dep:anstream",
+    "dep:anstyle",
+    "dep:is-terminal",
+    "dep:terminal_size",
+]
+default = ["std"]
+simd = ["dep:memchr"]
+std = [
+    "alloc",
+    "memchr?/std",
+]
+unstable-doc = [
+    "alloc",
+    "std",
+    "simd",
+    "unstable-recover",
+]
+unstable-recover = []
+
+[lib]
+name = "winnow"
+path = "src/lib.rs"
 
 [[example]]
 name = "arithmetic"
+path = "examples/arithmetic/main.rs"
 test = true
 required-features = ["alloc"]
 
 [[example]]
 name = "css"
+path = "examples/css/main.rs"
 test = true
 required-features = ["alloc"]
 
 [[example]]
 name = "custom_error"
+path = "examples/custom_error.rs"
 test = true
 required-features = ["alloc"]
 
 [[example]]
 name = "http"
+path = "examples/http/main.rs"
 required-features = ["alloc"]
 
 [[example]]
 name = "ini"
+path = "examples/ini/main.rs"
 test = true
 required-features = ["std"]
 
 [[example]]
+name = "iterator"
+path = "examples/iterator.rs"
+
+[[example]]
 name = "json"
-test = true
-required-features = ["std"]
-
-[[example]]
-name = "ndjson"
+path = "examples/json/main.rs"
 test = true
 required-features = ["std"]
 
 [[example]]
 name = "json_iterator"
+path = "examples/json_iterator.rs"
 required-features = ["std"]
 
 [[example]]
-name = "iterator"
+name = "ndjson"
+path = "examples/ndjson/main.rs"
+test = true
+required-features = ["std"]
 
 [[example]]
 name = "s_expression"
+path = "examples/s_expression/main.rs"
 required-features = ["alloc"]
 
 [[example]]
 name = "string"
+path = "examples/string/main.rs"
 required-features = ["alloc"]
 
 [[bench]]
@@ -152,22 +187,12 @@
 
 [[bench]]
 name = "contains_token"
+path = "benches/contains_token.rs"
 harness = false
 
 [[bench]]
 name = "find_slice"
-harness = false
-
-[[bench]]
-name = "iter"
-harness = false
-
-[[bench]]
-name = "next_slice"
-harness = false
-
-[[bench]]
-name = "number"
+path = "benches/find_slice.rs"
 harness = false
 
 [[bench]]
@@ -183,11 +208,26 @@
 required-features = ["std"]
 
 [[bench]]
+name = "iter"
+path = "benches/iter.rs"
+harness = false
+
+[[bench]]
 name = "json"
 path = "examples/json/bench.rs"
 harness = false
 required-features = ["std"]
 
+[[bench]]
+name = "next_slice"
+path = "benches/next_slice.rs"
+harness = false
+
+[[bench]]
+name = "number"
+path = "benches/number.rs"
+harness = false
+
 [dependencies.anstream]
 version = "0.3.2"
 optional = true
@@ -206,9 +246,18 @@
 default-features = false
 
 [dependencies.terminal_size]
-version = "0.2.6"
+version = "0.4.0"
 optional = true
 
+[dev-dependencies.annotate-snippets]
+version = "0.11.3"
+
+[dev-dependencies.anyhow]
+version = "1.0.86"
+
+[dev-dependencies.automod]
+version = "1.0.14"
+
 [dev-dependencies.circular]
 version = "0.3.0"
 
@@ -218,9 +267,6 @@
 [dev-dependencies.doc-comment]
 version = "0.3"
 
-[dev-dependencies.escargot]
-version = "0.5.7"
-
 [dev-dependencies.lexopt]
 version = "0.3.0"
 
@@ -231,30 +277,83 @@
 version = "1.1.0"
 
 [dev-dependencies.snapbox]
-version = "0.4.11"
+version = "0.6.0"
 features = ["examples"]
 
 [dev-dependencies.term-transcript]
 version = "0.2.0"
 
-[features]
-alloc = []
-debug = [
-    "dep:anstream",
-    "dep:anstyle",
-    "dep:is-terminal",
-    "dep:terminal_size",
-]
-default = ["std"]
-simd = ["dep:memchr"]
-std = [
-    "alloc",
-    "memchr?/std",
-]
-unstable-doc = [
-    "alloc",
-    "std",
-    "simd",
-    "unstable-recover",
-]
-unstable-recover = []
+[lints.clippy]
+bool_assert_comparison = "allow"
+branches_sharing_code = "allow"
+checked_conversions = "warn"
+collapsible_else_if = "allow"
+create_dir = "warn"
+dbg_macro = "warn"
+debug_assert_with_mut_call = "warn"
+doc_markdown = "warn"
+empty_enum = "warn"
+enum_glob_use = "warn"
+expl_impl_clone_on_copy = "warn"
+explicit_deref_methods = "warn"
+explicit_into_iter_loop = "warn"
+fallible_impl_from = "warn"
+filter_map_next = "warn"
+flat_map_option = "warn"
+float_cmp_const = "warn"
+fn_params_excessive_bools = "warn"
+from_iter_instead_of_collect = "warn"
+if_same_then_else = "allow"
+implicit_clone = "warn"
+imprecise_flops = "warn"
+inconsistent_struct_constructor = "warn"
+inefficient_to_string = "warn"
+infinite_loop = "warn"
+invalid_upcast_comparisons = "warn"
+large_digit_groups = "warn"
+large_stack_arrays = "warn"
+large_types_passed_by_value = "warn"
+let_and_return = "allow"
+linkedlist = "warn"
+lossy_float_literal = "warn"
+macro_use_imports = "warn"
+mem_forget = "warn"
+mutex_integer = "warn"
+needless_continue = "warn"
+needless_for_each = "warn"
+negative_feature_names = "warn"
+path_buf_push_overwrite = "warn"
+ptr_as_ptr = "warn"
+rc_mutex = "warn"
+redundant_feature_names = "warn"
+ref_option_ref = "warn"
+rest_pat_in_fully_bound_structs = "warn"
+result_large_err = "allow"
+same_functions_in_if_condition = "warn"
+semicolon_if_nothing_returned = "warn"
+str_to_string = "warn"
+string_add = "warn"
+string_add_assign = "warn"
+string_lit_as_bytes = "warn"
+string_to_string = "warn"
+todo = "warn"
+trait_duplication_in_bounds = "warn"
+uninlined_format_args = "warn"
+verbose_file_reads = "warn"
+wildcard_imports = "allow"
+zero_sized_map_values = "warn"
+
+[lints.rust]
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "warn"
+unused_lifetimes = "warn"
+unused_macro_rules = "warn"
+
+[lints.rust.rust_2018_idioms]
+level = "warn"
+priority = -1
+
+[profile.bench]
+lto = true
+codegen-units = 1
+debug = 2
diff --git a/crates/winnow/METADATA b/crates/winnow/METADATA
index d0decfb..15b0e37 100644
--- a/crates/winnow/METADATA
+++ b/crates/winnow/METADATA
@@ -1,17 +1,17 @@
 name: "winnow"
 description: "A byte-oriented, zero-copy, parser combinators library"
 third_party {
-  version: "0.5.37"
+  version: "0.6.24"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2024
-    month: 2
-    day: 6
+    year: 2025
+    month: 1
+    day: 14
   }
   homepage: "https://crates.io/crates/winnow"
   identifier {
     type: "Archive"
-    value: "https://static.crates.io/crates/winnow/winnow-0.5.37.crate"
-    version: "0.5.37"
+    value: "https://static.crates.io/crates/winnow/winnow-0.6.24.crate"
+    version: "0.6.24"
   }
 }
diff --git a/crates/winnow/benches/iter.rs b/crates/winnow/benches/iter.rs
index 0a0d5ff..6f42baa 100644
--- a/crates/winnow/benches/iter.rs
+++ b/crates/winnow/benches/iter.rs
@@ -107,7 +107,8 @@
         count += winnow::combinator::repeat(0.., one_of(AsChar::is_dec_digit))
             .map(|count: usize| count)
             .parse_next(input)?;
-        winnow::combinator::repeat(0.., one_of(|b: u8| !b.is_dec_digit())).parse_next(input)?;
+        let () =
+            winnow::combinator::repeat(0.., one_of(|b: u8| !b.is_dec_digit())).parse_next(input)?;
     }
     Ok(count)
 }
diff --git a/crates/winnow/benches/next_slice.rs b/crates/winnow/benches/next_slice.rs
index 6c25d23..b8f2e0b 100644
--- a/crates/winnow/benches/next_slice.rs
+++ b/crates/winnow/benches/next_slice.rs
@@ -2,8 +2,8 @@
 
 use winnow::combinator::repeat;
 use winnow::prelude::*;
+use winnow::token::literal;
 use winnow::token::one_of;
-use winnow::token::tag;
 
 fn next_slice(c: &mut criterion::Criterion) {
     let mut group = c.benchmark_group("next_slice");
@@ -102,11 +102,11 @@
 }
 
 fn parser_ascii_tag_char(input: &mut &str) -> PResult<usize> {
-    repeat(0.., tag('h')).parse_next(input)
+    repeat(0.., literal('h')).parse_next(input)
 }
 
 fn parser_ascii_tag_str(input: &mut &str) -> PResult<usize> {
-    repeat(0.., tag("h")).parse_next(input)
+    repeat(0.., literal("h")).parse_next(input)
 }
 
 fn parser_utf8_char(input: &mut &str) -> PResult<usize> {
@@ -122,11 +122,11 @@
 }
 
 fn parser_utf8_tag_char(input: &mut &str) -> PResult<usize> {
-    repeat(0.., tag('🧑')).parse_next(input)
+    repeat(0.., literal('🧑')).parse_next(input)
 }
 
 fn parser_utf8_tag_str(input: &mut &str) -> PResult<usize> {
-    repeat(0.., tag("🧑")).parse_next(input)
+    repeat(0.., literal("🧑")).parse_next(input)
 }
 
 criterion::criterion_group!(benches, next_slice);
diff --git a/crates/winnow/examples/arithmetic/main.rs b/crates/winnow/examples/arithmetic/main.rs
index e46cf2f..046ad8c 100644
--- a/crates/winnow/examples/arithmetic/main.rs
+++ b/crates/winnow/examples/arithmetic/main.rs
@@ -10,7 +10,7 @@
     let input = args.input.as_deref().unwrap_or("1 + 1");
     if let Err(err) = calc(input, args.implementation) {
         println!("FAILED");
-        println!("{}", err);
+        println!("{err}");
     }
 
     Ok(())
@@ -20,11 +20,11 @@
     input: &str,
     imp: Impl,
 ) -> Result<(), winnow::error::ParseError<&str, winnow::error::ContextError>> {
-    println!("{} =", input);
+    println!("{input} =");
     match imp {
         Impl::Eval => {
             let result = parser::expr.parse(input)?;
-            println!("  {}", result);
+            println!("  {result}");
         }
         Impl::Ast => {
             let result = parser_ast::expr.parse(input)?;
@@ -32,7 +32,7 @@
         }
         Impl::Lexer => {
             let tokens = parser_lexer::lex.parse(input)?;
-            println!("  {:#?}", tokens);
+            println!("  {tokens:#?}");
             let result = parser_lexer::expr.parse(tokens.as_slice()).unwrap();
             println!("  {:#?}={}", result, result.eval());
         }
diff --git a/crates/winnow/examples/arithmetic/parser.rs b/crates/winnow/examples/arithmetic/parser.rs
index d51007a..2e174d8 100644
--- a/crates/winnow/examples/arithmetic/parser.rs
+++ b/crates/winnow/examples/arithmetic/parser.rs
@@ -11,7 +11,7 @@
 
 // Parser definition
 
-pub fn expr(i: &mut &str) -> PResult<i64> {
+pub(crate) fn expr(i: &mut &str) -> PResult<i64> {
     let init = term.parse_next(i)?;
 
     repeat(0.., (one_of(['+', '-']), term))
diff --git a/crates/winnow/examples/arithmetic/parser_ast.rs b/crates/winnow/examples/arithmetic/parser_ast.rs
index 20feb26..251d730 100644
--- a/crates/winnow/examples/arithmetic/parser_ast.rs
+++ b/crates/winnow/examples/arithmetic/parser_ast.rs
@@ -13,7 +13,7 @@
 };
 
 #[derive(Debug, Clone)]
-pub enum Expr {
+pub(crate) enum Expr {
     Value(i64),
     Add(Box<Expr>, Box<Expr>),
     Sub(Box<Expr>, Box<Expr>),
@@ -23,7 +23,7 @@
 }
 
 impl Expr {
-    pub fn eval(&self) -> i64 {
+    pub(crate) fn eval(&self) -> i64 {
         match self {
             Self::Value(v) => *v,
             Self::Add(lhs, rhs) => lhs.eval() + rhs.eval(),
@@ -39,17 +39,17 @@
     fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
         use Expr::{Add, Div, Mul, Paren, Sub, Value};
         match *self {
-            Value(val) => write!(format, "{}", val),
-            Add(ref left, ref right) => write!(format, "{} + {}", left, right),
-            Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
-            Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
-            Div(ref left, ref right) => write!(format, "{} / {}", left, right),
-            Paren(ref expr) => write!(format, "({})", expr),
+            Value(val) => write!(format, "{val}"),
+            Add(ref left, ref right) => write!(format, "{left} + {right}"),
+            Sub(ref left, ref right) => write!(format, "{left} - {right}"),
+            Mul(ref left, ref right) => write!(format, "{left} * {right}"),
+            Div(ref left, ref right) => write!(format, "{left} / {right}"),
+            Paren(ref expr) => write!(format, "({expr})"),
         }
     }
 }
 
-pub fn expr(i: &mut &str) -> PResult<Expr> {
+pub(crate) fn expr(i: &mut &str) -> PResult<Expr> {
     let init = term.parse_next(i)?;
 
     repeat(0.., (one_of(['+', '-']), term))
diff --git a/crates/winnow/examples/arithmetic/parser_lexer.rs b/crates/winnow/examples/arithmetic/parser_lexer.rs
index d6b7422..3e3d842 100644
--- a/crates/winnow/examples/arithmetic/parser_lexer.rs
+++ b/crates/winnow/examples/arithmetic/parser_lexer.rs
@@ -17,7 +17,7 @@
 };
 
 #[derive(Debug, Clone)]
-pub enum Expr {
+pub(crate) enum Expr {
     Value(i64),
     Add(Box<Expr>, Box<Expr>),
     Sub(Box<Expr>, Box<Expr>),
@@ -27,7 +27,7 @@
 }
 
 impl Expr {
-    pub fn eval(&self) -> i64 {
+    pub(crate) fn eval(&self) -> i64 {
         match self {
             Self::Value(v) => *v,
             Self::Add(lhs, rhs) => lhs.eval() + rhs.eval(),
@@ -43,12 +43,12 @@
     fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result {
         use Expr::{Add, Div, Mul, Paren, Sub, Value};
         match *self {
-            Value(val) => write!(format, "{}", val),
-            Add(ref left, ref right) => write!(format, "{} + {}", left, right),
-            Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
-            Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
-            Div(ref left, ref right) => write!(format, "{} / {}", left, right),
-            Paren(ref expr) => write!(format, "({})", expr),
+            Value(val) => write!(format, "{val}"),
+            Add(ref left, ref right) => write!(format, "{left} + {right}"),
+            Sub(ref left, ref right) => write!(format, "{left} - {right}"),
+            Mul(ref left, ref right) => write!(format, "{left} * {right}"),
+            Div(ref left, ref right) => write!(format, "{left} / {right}"),
+            Paren(ref expr) => write!(format, "({expr})"),
         }
     }
 }
@@ -98,12 +98,12 @@
 }
 
 #[allow(dead_code)]
-pub fn expr2(i: &mut &str) -> PResult<Expr> {
+pub(crate) fn expr2(i: &mut &str) -> PResult<Expr> {
     let tokens = lex.parse_next(i)?;
     expr.parse_next(&mut tokens.as_slice())
 }
 
-pub fn lex(i: &mut &str) -> PResult<Vec<Token>> {
+pub(crate) fn lex(i: &mut &str) -> PResult<Vec<Token>> {
     preceded(multispaces, repeat(1.., terminated(token, multispaces))).parse_next(i)
 }
 
@@ -121,7 +121,7 @@
     .parse_next(i)
 }
 
-pub fn expr(i: &mut &[Token]) -> PResult<Expr> {
+pub(crate) fn expr(i: &mut &[Token]) -> PResult<Expr> {
     let init = term.parse_next(i)?;
 
     repeat(
diff --git a/crates/winnow/examples/css/main.rs b/crates/winnow/examples/css/main.rs
index cf52ada..f6e981d 100644
--- a/crates/winnow/examples/css/main.rs
+++ b/crates/winnow/examples/css/main.rs
@@ -9,13 +9,13 @@
 
     let input = args.input.as_deref().unwrap_or("#AAAAAA");
 
-    println!("{} =", input);
+    println!("{input} =");
     match hex_color.parse(input) {
         Ok(result) => {
-            println!("  {:?}", result);
+            println!("  {result:?}");
         }
         Err(err) => {
-            println!("  {}", err);
+            println!("  {err}");
         }
     }
 
diff --git a/crates/winnow/examples/css/parser.rs b/crates/winnow/examples/css/parser.rs
index fa13078..8088de5 100644
--- a/crates/winnow/examples/css/parser.rs
+++ b/crates/winnow/examples/css/parser.rs
@@ -3,10 +3,10 @@
 use winnow::token::take_while;
 
 #[derive(Debug, Eq, PartialEq)]
-pub struct Color {
-    pub red: u8,
-    pub green: u8,
-    pub blue: u8,
+pub(crate) struct Color {
+    pub(crate) red: u8,
+    pub(crate) green: u8,
+    pub(crate) blue: u8,
 }
 
 impl std::str::FromStr for Color {
@@ -18,7 +18,7 @@
     }
 }
 
-pub fn hex_color(input: &mut &str) -> PResult<Color> {
+pub(crate) fn hex_color(input: &mut &str) -> PResult<Color> {
     seq!(Color {
         _: '#',
         red: hex_primary,
diff --git a/crates/winnow/examples/custom_error.rs b/crates/winnow/examples/custom_error.rs
index 998e5ad..a728a7e 100644
--- a/crates/winnow/examples/custom_error.rs
+++ b/crates/winnow/examples/custom_error.rs
@@ -1,24 +1,57 @@
+use winnow::error::AddContext;
 use winnow::error::ErrMode;
 use winnow::error::ErrorKind;
+use winnow::error::FromExternalError;
 use winnow::error::ParserError;
 use winnow::prelude::*;
+use winnow::stream::Stream;
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug)]
 pub enum CustomError<I> {
     MyError,
-    Nom(I, ErrorKind),
+    Winnow(I, ErrorKind),
+    External {
+        cause: Box<dyn std::error::Error + Send + Sync + 'static>,
+        input: I,
+        kind: ErrorKind,
+    },
 }
 
-impl<I: Clone> ParserError<I> for CustomError<I> {
+impl<I: Stream + Clone> ParserError<I> for CustomError<I> {
     fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
-        CustomError::Nom(input.clone(), kind)
+        CustomError::Winnow(input.clone(), kind)
     }
 
-    fn append(self, _: &I, _: ErrorKind) -> Self {
+    fn append(self, _: &I, _: &<I as Stream>::Checkpoint, _: ErrorKind) -> Self {
         self
     }
 }
 
+impl<C, I: Stream> AddContext<I, C> for CustomError<I> {
+    #[inline]
+    fn add_context(
+        self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        _context: C,
+    ) -> Self {
+        self
+    }
+}
+
+impl<I: Stream + Clone, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E>
+    for CustomError<I>
+{
+    #[inline]
+    fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self {
+        CustomError::External {
+            cause: Box::new(e),
+            input: input.clone(),
+            kind,
+        }
+    }
+}
+
 pub fn parse<'s>(_input: &mut &'s str) -> PResult<&'s str, CustomError<&'s str>> {
     Err(ErrMode::Backtrack(CustomError::MyError))
 }
@@ -32,9 +65,6 @@
     #[test]
     fn it_works() {
         let err = parse.parse_next(&mut "").unwrap_err();
-        match err {
-            ErrMode::Backtrack(e) => assert_eq!(e, CustomError::MyError),
-            _ => panic!("Unexpected error: {:?}", err),
-        }
+        assert!(matches!(err, ErrMode::Backtrack(CustomError::MyError)));
     }
 }
diff --git a/crates/winnow/examples/http/main.rs b/crates/winnow/examples/http/main.rs
index b0e480f..02858c2 100644
--- a/crates/winnow/examples/http/main.rs
+++ b/crates/winnow/examples/http/main.rs
@@ -16,7 +16,7 @@
     );
 
     if let Some(result) = parser::parse(input.as_bytes()) {
-        println!("  {:#?}", result);
+        println!("  {result:#?}");
     }
 
     Ok(())
diff --git a/crates/winnow/examples/http/parser.rs b/crates/winnow/examples/http/parser.rs
index 0afe33b..4c790f4 100644
--- a/crates/winnow/examples/http/parser.rs
+++ b/crates/winnow/examples/http/parser.rs
@@ -2,12 +2,12 @@
 use winnow::prelude::*;
 use winnow::{ascii::line_ending, combinator::repeat, token::take_while};
 
-pub type Stream<'i> = &'i [u8];
+pub(crate) type Stream<'i> = &'i [u8];
 
 #[rustfmt::skip]
 #[derive(Debug)]
 #[allow(dead_code)]
-pub struct Request<'a> {
+pub(crate) struct Request<'a> {
   method:  &'a [u8],
   uri:     &'a [u8],
   version: &'a [u8],
@@ -15,12 +15,12 @@
 
 #[derive(Debug)]
 #[allow(dead_code)]
-pub struct Header<'a> {
+pub(crate) struct Header<'a> {
     name: &'a [u8],
     value: Vec<&'a [u8]>,
 }
 
-pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
+pub(crate) fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
     let mut buf = data;
     let mut v = Vec::new();
     loop {
@@ -34,7 +34,7 @@
                 }
             }
             Err(e) => {
-                println!("error: {:?}", e);
+                println!("error: {e:?}");
                 return None;
             }
         }
diff --git a/crates/winnow/examples/http/parser_streaming.rs b/crates/winnow/examples/http/parser_streaming.rs
index 1079bc0..361730e 100644
--- a/crates/winnow/examples/http/parser_streaming.rs
+++ b/crates/winnow/examples/http/parser_streaming.rs
@@ -3,12 +3,12 @@
     ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while,
 };
 
-pub type Stream<'i> = Partial<&'i [u8]>;
+pub(crate) type Stream<'i> = Partial<&'i [u8]>;
 
 #[rustfmt::skip]
 #[derive(Debug)]
 #[allow(dead_code)]
-pub struct Request<'a> {
+pub(crate) struct Request<'a> {
   method:  &'a [u8],
   uri:     &'a [u8],
   version: &'a [u8],
@@ -16,12 +16,12 @@
 
 #[derive(Debug)]
 #[allow(dead_code)]
-pub struct Header<'a> {
+pub(crate) struct Header<'a> {
     name: &'a [u8],
     value: Vec<&'a [u8]>,
 }
 
-pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
+pub(crate) fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
     let mut buf = Partial::new(data);
     let mut v = Vec::new();
     loop {
@@ -35,7 +35,7 @@
                 }
             }
             Err(e) => {
-                println!("error: {:?}", e);
+                println!("error: {e:?}");
                 return None;
             }
         }
diff --git a/crates/winnow/examples/ini/bench.rs b/crates/winnow/examples/ini/bench.rs
index 3c7eab8..b6ccf28 100644
--- a/crates/winnow/examples/ini/bench.rs
+++ b/crates/winnow/examples/ini/bench.rs
@@ -21,7 +21,7 @@
         b.iter(|| parser::categories.parse_peek(str.as_bytes()).unwrap());
     });
     group.bench_function(criterion::BenchmarkId::new("str", str.len()), |b| {
-        b.iter(|| parser_str::categories.parse_peek(str).unwrap())
+        b.iter(|| parser_str::categories.parse_peek(str).unwrap());
     });
 }
 
diff --git a/crates/winnow/examples/ini/main.rs b/crates/winnow/examples/ini/main.rs
index 8f61d18..5e18887 100644
--- a/crates/winnow/examples/ini/main.rs
+++ b/crates/winnow/examples/ini/main.rs
@@ -11,19 +11,19 @@
     if args.binary {
         match parser::categories.parse(input.as_bytes()) {
             Ok(result) => {
-                println!("  {:?}", result);
+                println!("  {result:?}");
             }
             Err(err) => {
-                println!("  {:?}", err);
+                println!("  {err:?}");
             }
         }
     } else {
         match parser_str::categories.parse(input) {
             Ok(result) => {
-                println!("  {:?}", result);
+                println!("  {result:?}");
             }
             Err(err) => {
-                println!("  {}", err);
+                println!("  {err}");
             }
         }
     }
diff --git a/crates/winnow/examples/ini/parser.rs b/crates/winnow/examples/ini/parser.rs
index a782d6e..13b552e 100644
--- a/crates/winnow/examples/ini/parser.rs
+++ b/crates/winnow/examples/ini/parser.rs
@@ -10,9 +10,11 @@
     token::take_while,
 };
 
-pub type Stream<'i> = &'i [u8];
+pub(crate) type Stream<'i> = &'i [u8];
 
-pub fn categories<'s>(i: &mut Stream<'s>) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> {
+pub(crate) fn categories<'s>(
+    i: &mut Stream<'s>,
+) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> {
     repeat(
         0..,
         separated_pair(
@@ -30,7 +32,7 @@
         .parse_next(i)
 }
 
-pub fn key_value<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, &'s str)> {
+pub(crate) fn key_value<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, &'s str)> {
     let key = alphanumeric.try_map(str::from_utf8).parse_next(i)?;
     let _ = (opt(space), '=', opt(space)).parse_next(i)?;
     let val = take_while(0.., |c| c != b'\n' && c != b';')
@@ -51,7 +53,7 @@
 key = value2"[..];
 
     let res = category.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
         Ok((i, o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
         _ => println!("error"),
@@ -68,7 +70,7 @@
     let ini_without_key_value = &b"\nkey = value2"[..];
 
     let res = key_value.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
         Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
         _ => println!("error"),
@@ -85,7 +87,7 @@
     let ini_without_key_value = &b"\nkey = value2"[..];
 
     let res = key_value.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
         Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
         _ => println!("error"),
@@ -102,7 +104,7 @@
     let ini_without_key_value = &b"\nkey = value2"[..];
 
     let res = key_value.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
         Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
         _ => println!("error"),
diff --git a/crates/winnow/examples/ini/parser_str.rs b/crates/winnow/examples/ini/parser_str.rs
index 7c3603c..8af75a5 100644
--- a/crates/winnow/examples/ini/parser_str.rs
+++ b/crates/winnow/examples/ini/parser_str.rs
@@ -9,9 +9,9 @@
     token::{take_till, take_while},
 };
 
-pub type Stream<'i> = &'i str;
+pub(crate) type Stream<'i> = &'i str;
 
-pub fn categories<'s>(
+pub(crate) fn categories<'s>(
     input: &mut Stream<'s>,
 ) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> {
     repeat(0.., category_and_keys).parse_next(input)
@@ -67,9 +67,9 @@
 key = value2";
 
     let res = category.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
-        Ok((i, o)) => println!("i: {} | o: {:?}", i, o),
+        Ok((i, o)) => println!("i: {i} | o: {o:?}"),
         _ => println!("error"),
     }
 
@@ -84,9 +84,9 @@
     let ini_without_key_value = "key = value2";
 
     let res = key_value.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
-        Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
+        Ok((i, (o1, o2))) => println!("i: {i} | o: ({o1:?},{o2:?})"),
         _ => println!("error"),
     }
 
@@ -101,9 +101,9 @@
     let ini_without_key_value = "key = value2";
 
     let res = key_value.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
-        Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
+        Ok((i, (o1, o2))) => println!("i: {i} | o: ({o1:?},{o2:?})"),
         _ => println!("error"),
     }
 
@@ -118,9 +118,9 @@
     let ini_without_key_value = "key = value2";
 
     let res = key_value.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
-        Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
+        Ok((i, (o1, o2))) => println!("i: {i} | o: ({o1:?},{o2:?})"),
         _ => println!("error"),
     }
 
@@ -138,9 +138,9 @@
     let ini_without_key_value = "[category]";
 
     let res = keys_and_values.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
-        Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
+        Ok((i, ref o)) => println!("i: {i} | o: {o:?}"),
         _ => println!("error"),
     }
 
@@ -163,9 +163,9 @@
     let ini_after_parser = "[category]";
 
     let res = category_and_keys.parse_peek(ini_file);
-    println!("{:?}", res);
+    println!("{res:?}");
     match res {
-        Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
+        Ok((i, ref o)) => println!("i: {i} | o: {o:?}"),
         _ => println!("error"),
     }
 
@@ -191,7 +191,7 @@
     let res = categories.parse_peek(ini_file);
     //println!("{:?}", res);
     match res {
-        Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
+        Ok((i, ref o)) => println!("i: {i} | o: {o:?}"),
         _ => println!("error"),
     }
 
diff --git a/crates/winnow/examples/iterator.rs b/crates/winnow/examples/iterator.rs
index 5c8c731..a394f71 100644
--- a/crates/winnow/examples/iterator.rs
+++ b/crates/winnow/examples/iterator.rs
@@ -27,7 +27,7 @@
     });
 
     for value in it {
-        println!("parser returned: {}", value);
+        println!("parser returned: {value}");
     }
 
     println!("\n********************\n");
@@ -46,7 +46,7 @@
         });
 
     // will print "parser iterator returned: Ok(("abc", ["abc", "abc", "abc"]))"
-    println!("\nparser iterator returned: {:?}", res);
+    println!("\nparser iterator returned: {res:?}");
 
     println!("\n********************\n");
 
@@ -70,8 +70,5 @@
     let (remaining_input, ()) = parser_result.unwrap();
 
     // will print "iterator returned {"key1": "value1", "key3": "value3", "key2": "value2"}, remaining input is ';'"
-    println!(
-        "iterator returned {:?}, remaining input is '{}'",
-        res, remaining_input
-    );
+    println!("iterator returned {res:?}, remaining input is '{remaining_input}'");
 }
diff --git a/crates/winnow/examples/json/bench.rs b/crates/winnow/examples/json/bench.rs
index d074ba5..c789da2 100644
--- a/crates/winnow/examples/json/bench.rs
+++ b/crates/winnow/examples/json/bench.rs
@@ -13,21 +13,16 @@
         let len = sample.len();
         group.throughput(criterion::Throughput::Bytes(len as u64));
 
-        group.bench_with_input(criterion::BenchmarkId::new("basic", name), &len, |b, _| {
-            type Error<'i> = winnow::error::InputError<parser::Stream<'i>>;
-
-            b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
-        });
         group.bench_with_input(criterion::BenchmarkId::new("unit", name), &len, |b, _| {
             type Error<'i> = ();
 
-            b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
+            b.iter(|| parser::json::<Error<'_>>.parse_peek(sample).unwrap());
         });
         group.bench_with_input(
             criterion::BenchmarkId::new("context", name),
             &len,
             |b, _| {
-                type Error<'i> = winnow::error::ContextError<parser::Stream<'i>>;
+                type Error = winnow::error::ContextError;
 
                 b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
             },
@@ -36,7 +31,7 @@
             criterion::BenchmarkId::new("dispatch", name),
             &len,
             |b, _| {
-                type Error<'i> = winnow::error::InputError<parser_dispatch::Stream<'i>>;
+                type Error = winnow::error::ContextError;
 
                 b.iter(|| parser_dispatch::json::<Error>.parse_peek(sample).unwrap());
             },
@@ -45,7 +40,7 @@
             criterion::BenchmarkId::new("streaming", name),
             &len,
             |b, _| {
-                type Error<'i> = winnow::error::InputError<parser_partial::Stream<'i>>;
+                type Error = winnow::error::ContextError;
 
                 b.iter(|| {
                     parser_partial::json::<Error>
diff --git a/crates/winnow/examples/json/json.rs b/crates/winnow/examples/json/json.rs
index 6912d60..2611171 100644
--- a/crates/winnow/examples/json/json.rs
+++ b/crates/winnow/examples/json/json.rs
@@ -1,7 +1,7 @@
 use std::collections::HashMap;
 
 #[derive(Debug, PartialEq, Clone)]
-pub enum JsonValue {
+pub(crate) enum JsonValue {
     Null,
     Boolean(bool),
     Str(String),
diff --git a/crates/winnow/examples/json/main.rs b/crates/winnow/examples/json/main.rs
index be431e8..5f90f74 100644
--- a/crates/winnow/examples/json/main.rs
+++ b/crates/winnow/examples/json/main.rs
@@ -30,13 +30,13 @@
     };
     match result {
         Ok(json) => {
-            println!("{:#?}", json);
+            println!("{json:#?}");
         }
         Err(err) => {
             if args.pretty {
-                println!("{}", err);
+                println!("{err}");
             } else {
-                println!("{:#?}", err);
+                println!("{err:#?}");
             }
         }
     }
diff --git a/crates/winnow/examples/json/parser.rs b/crates/winnow/examples/json/parser.rs
index e8d9c8a..a9597f2 100644
--- a/crates/winnow/examples/json/parser.rs
+++ b/crates/winnow/examples/json/parser.rs
@@ -8,18 +8,18 @@
     combinator::cut_err,
     combinator::{delimited, preceded, separated_pair, terminated},
     combinator::{repeat, separated},
-    error::{AddContext, ParserError},
+    error::{AddContext, ParserError, StrContext},
     token::{any, none_of, take, take_while},
 };
 
 use crate::json::JsonValue;
 
-pub type Stream<'i> = &'i str;
+pub(crate) type Stream<'i> = &'i str;
 
 /// The root element of a JSON parser is any value
 ///
 /// A parser has the following signature:
-/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
+/// `&mut Stream -> PResult<Output, ContextError>`, with `PResult` defined as:
 /// `type PResult<O, E = (I, ErrorKind)> = Result<O, Err<E>>;`
 ///
 /// most of the times you can ignore the error type and use the default (but this
@@ -28,7 +28,7 @@
 /// Here we use `&str` as input type, but parsers can be generic over
 /// the input type, work directly with `&[u8]`, or any other type that
 /// implements the required traits.
-pub fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     delimited(ws, json_value, ws).parse_next(input)
@@ -36,7 +36,7 @@
 
 /// `alt` is a combinator that tries multiple parsers one by one, until
 /// one of them succeeds
-fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     // `alt` combines the each value parser. It returns the result of the first
@@ -52,7 +52,7 @@
     .parse_next(input)
 }
 
-/// `tag(string)` generates a parser that recognizes the argument string.
+/// `literal(string)` generates a parser that takes the argument string.
 ///
 /// This also shows returning a sub-slice of the original input
 fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
@@ -75,9 +75,9 @@
     alt((parse_true, parse_false)).parse_next(input)
 }
 
-/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// This parser gathers all `char`s up into a `String`with a parse to take the double quote
 /// character, before the string (using `preceded`) and after the string (using `terminated`).
-fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<String, E> {
     preceded(
@@ -96,7 +96,7 @@
     )
     // `context` lets you add a static string to errors to provide more information in the
     // error chain (to indicate which parser had an error)
-    .context("string")
+    .context(StrContext::Expected("string".into()))
     .parse_next(input)
 }
 
@@ -157,7 +157,7 @@
 /// accumulating results in a `Vec`, until it encounters an error.
 /// If you want more control on the parser application, check out the `iterator`
 /// combinator (cf `examples/iterator.rs`)
-fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<Vec<JsonValue>, E> {
     preceded(
@@ -167,11 +167,11 @@
             (ws, ']'),
         )),
     )
-    .context("array")
+    .context(StrContext::Expected("array".into()))
     .parse_next(input)
 }
 
-fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<HashMap<String, JsonValue>, E> {
     preceded(
@@ -181,11 +181,11 @@
             (ws, '}'),
         )),
     )
-    .context("object")
+    .context(StrContext::Expected("object".into()))
     .parse_next(input)
 }
 
-fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<(String, JsonValue), E> {
     separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
@@ -205,42 +205,37 @@
 #[cfg(test)]
 mod test {
     #[allow(clippy::useless_attribute)]
-    #[allow(dead_code)] // its dead for benches
+    #[allow(unused_imports)] // its dead for benches
     use super::*;
 
     #[allow(clippy::useless_attribute)]
     #[allow(dead_code)] // its dead for benches
-    type Error<'i> = winnow::error::InputError<&'i str>;
+    type Error = winnow::error::ContextError;
 
     #[test]
     fn json_string() {
+        assert_eq!(string::<Error>.parse_peek("\"\""), Ok(("", "".to_owned())));
         assert_eq!(
-            string::<Error<'_>>.parse_peek("\"\""),
-            Ok(("", "".to_string()))
+            string::<Error>.parse_peek("\"abc\""),
+            Ok(("", "abc".to_owned()))
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek("\"abc\""),
-            Ok(("", "abc".to_string()))
-        );
-        assert_eq!(
-            string::<Error<'_>>
+            string::<Error>
                 .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""),
-            Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string())),
+            Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())),
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""),
-            Ok(("", "😐".to_string()))
+            string::<Error>.parse_peek("\"\\uD83D\\uDE10\""),
+            Ok(("", "😐".to_owned()))
         );
 
-        assert!(string::<Error<'_>>.parse_peek("\"").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"abc").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\\"").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\u123\"").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\uD800\"").is_err());
-        assert!(string::<Error<'_>>
-            .parse_peek("\"\\uD800\\uD800\"")
-            .is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\uDC00\"").is_err());
+        assert!(string::<Error>.parse_peek("\"").is_err());
+        assert!(string::<Error>.parse_peek("\"abc").is_err());
+        assert!(string::<Error>.parse_peek("\"\\\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\u123\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\uD800\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\uD800\\uD800\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\uDC00\"").is_err());
     }
 
     #[test]
@@ -251,14 +246,14 @@
 
         let expected = Object(
             vec![
-                ("a".to_string(), Num(42.0)),
-                ("b".to_string(), Str("x".to_string())),
+                ("a".to_owned(), Num(42.0)),
+                ("b".to_owned(), Str("x".to_owned())),
             ]
             .into_iter()
             .collect(),
         );
 
-        assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+        assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
     }
 
     #[test]
@@ -267,9 +262,9 @@
 
         let input = r#"[42,"x"]"#;
 
-        let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+        let expected = Array(vec![Num(42.0), Str("x".to_owned())]);
 
-        assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+        assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
     }
 
     #[test]
@@ -291,33 +286,33 @@
   "#;
 
         assert_eq!(
-            json::<Error<'_>>.parse_peek(input),
+            json::<Error>.parse_peek(input),
             Ok((
                 "",
                 Object(
                     vec![
-                        ("null".to_string(), Null),
-                        ("true".to_string(), Boolean(true)),
-                        ("false".to_string(), Boolean(false)),
-                        ("number".to_string(), Num(123e4)),
-                        ("string".to_string(), Str(" abc 123 ".to_string())),
+                        ("null".to_owned(), Null),
+                        ("true".to_owned(), Boolean(true)),
+                        ("false".to_owned(), Boolean(false)),
+                        ("number".to_owned(), Num(123e4)),
+                        ("string".to_owned(), Str(" abc 123 ".to_owned())),
                         (
-                            "array".to_string(),
-                            Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+                            "array".to_owned(),
+                            Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())])
                         ),
                         (
-                            "object".to_string(),
+                            "object".to_owned(),
                             Object(
                                 vec![
-                                    ("a".to_string(), Num(1.0)),
-                                    ("b".to_string(), Str("c".to_string())),
+                                    ("a".to_owned(), Num(1.0)),
+                                    ("b".to_owned(), Str("c".to_owned())),
                                 ]
                                 .into_iter()
                                 .collect()
                             )
                         ),
-                        ("empty_array".to_string(), Array(vec![]),),
-                        ("empty_object".to_string(), Object(HashMap::new()),),
+                        ("empty_array".to_owned(), Array(vec![]),),
+                        ("empty_object".to_owned(), Object(HashMap::new()),),
                     ]
                     .into_iter()
                     .collect()
diff --git a/crates/winnow/examples/json/parser_dispatch.rs b/crates/winnow/examples/json/parser_dispatch.rs
index 11bda4f..06b391b 100644
--- a/crates/winnow/examples/json/parser_dispatch.rs
+++ b/crates/winnow/examples/json/parser_dispatch.rs
@@ -11,18 +11,18 @@
     combinator::{alt, dispatch},
     combinator::{delimited, preceded, separated_pair, terminated},
     combinator::{repeat, separated},
-    error::{AddContext, ParserError},
+    error::{AddContext, ParserError, StrContext},
     token::{any, none_of, take, take_while},
 };
 
 use crate::json::JsonValue;
 
-pub type Stream<'i> = &'i str;
+pub(crate) type Stream<'i> = &'i str;
 
 /// The root element of a JSON parser is any value
 ///
 /// A parser has the following signature:
-/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
+/// `&mut Stream -> PResult<Output ContextError>`, with `PResult` defined as:
 /// `type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;`
 ///
 /// most of the times you can ignore the error type and use the default (but this
@@ -31,7 +31,7 @@
 /// Here we use `&str` as input type, but parsers can be generic over
 /// the input type, work directly with `&[u8]`, or any other type that
 /// implements the required traits.
-pub fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     delimited(ws, json_value, ws).parse_next(input)
@@ -39,7 +39,7 @@
 
 /// `alt` is a combinator that tries multiple parsers one by one, until
 /// one of them succeeds
-fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     // `dispatch` gives you `match`-like behavior compared to `alt` successively trying different
@@ -59,7 +59,7 @@
     .parse_next(input)
 }
 
-/// `tag(string)` generates a parser that recognizes the argument string.
+/// `literal(string)` generates a parser that takes the argument string.
 ///
 /// This also shows returning a sub-slice of the original input
 fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
@@ -84,9 +84,9 @@
     "false".value(false).parse_next(input)
 }
 
-/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// This parser gathers all `char`s up into a `String`with a parse to take the double quote
 /// character, before the string (using `preceded`) and after the string (using `terminated`).
-fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<String, E> {
     preceded(
@@ -105,7 +105,7 @@
     )
     // `context` lets you add a static string to errors to provide more information in the
     // error chain (to indicate which parser had an error)
-    .context("string")
+    .context(StrContext::Expected("string".into()))
     .parse_next(input)
 }
 
@@ -164,7 +164,7 @@
 /// accumulating results in a `Vec`, until it encounters an error.
 /// If you want more control on the parser application, check out the `iterator`
 /// combinator (cf `examples/iterator.rs`)
-fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<Vec<JsonValue>, E> {
     preceded(
@@ -174,11 +174,11 @@
             (ws, ']'),
         )),
     )
-    .context("array")
+    .context(StrContext::Expected("array".into()))
     .parse_next(input)
 }
 
-fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<HashMap<String, JsonValue>, E> {
     preceded(
@@ -188,11 +188,11 @@
             (ws, '}'),
         )),
     )
-    .context("object")
+    .context(StrContext::Expected("object".into()))
     .parse_next(input)
 }
 
-fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<(String, JsonValue), E> {
     separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
@@ -212,42 +212,37 @@
 #[cfg(test)]
 mod test {
     #[allow(clippy::useless_attribute)]
-    #[allow(dead_code)] // its dead for benches
+    #[allow(unused_imports)] // its dead for benches
     use super::*;
 
     #[allow(clippy::useless_attribute)]
     #[allow(dead_code)] // its dead for benches
-    type Error<'i> = winnow::error::InputError<&'i str>;
+    type Error = winnow::error::ContextError;
 
     #[test]
     fn json_string() {
+        assert_eq!(string::<Error>.parse_peek("\"\""), Ok(("", "".to_owned())));
         assert_eq!(
-            string::<Error<'_>>.parse_peek("\"\""),
-            Ok(("", "".to_string()))
+            string::<Error>.parse_peek("\"abc\""),
+            Ok(("", "abc".to_owned()))
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek("\"abc\""),
-            Ok(("", "abc".to_string()))
-        );
-        assert_eq!(
-            string::<Error<'_>>
+            string::<Error>
                 .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""),
-            Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string())),
+            Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())),
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""),
-            Ok(("", "😐".to_string()))
+            string::<Error>.parse_peek("\"\\uD83D\\uDE10\""),
+            Ok(("", "😐".to_owned()))
         );
 
-        assert!(string::<Error<'_>>.parse_peek("\"").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"abc").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\\"").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\u123\"").is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\uD800\"").is_err());
-        assert!(string::<Error<'_>>
-            .parse_peek("\"\\uD800\\uD800\"")
-            .is_err());
-        assert!(string::<Error<'_>>.parse_peek("\"\\uDC00\"").is_err());
+        assert!(string::<Error>.parse_peek("\"").is_err());
+        assert!(string::<Error>.parse_peek("\"abc").is_err());
+        assert!(string::<Error>.parse_peek("\"\\\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\u123\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\uD800\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\uD800\\uD800\"").is_err());
+        assert!(string::<Error>.parse_peek("\"\\uDC00\"").is_err());
     }
 
     #[test]
@@ -258,14 +253,14 @@
 
         let expected = Object(
             vec![
-                ("a".to_string(), Num(42.0)),
-                ("b".to_string(), Str("x".to_string())),
+                ("a".to_owned(), Num(42.0)),
+                ("b".to_owned(), Str("x".to_owned())),
             ]
             .into_iter()
             .collect(),
         );
 
-        assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+        assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
     }
 
     #[test]
@@ -274,9 +269,9 @@
 
         let input = r#"[42,"x"]"#;
 
-        let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+        let expected = Array(vec![Num(42.0), Str("x".to_owned())]);
 
-        assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+        assert_eq!(json::<Error>.parse_peek(input), Ok(("", expected)));
     }
 
     #[test]
@@ -298,33 +293,33 @@
   "#;
 
         assert_eq!(
-            json::<Error<'_>>.parse_peek(input),
+            json::<Error>.parse_peek(input),
             Ok((
                 "",
                 Object(
                     vec![
-                        ("null".to_string(), Null),
-                        ("true".to_string(), Boolean(true)),
-                        ("false".to_string(), Boolean(false)),
-                        ("number".to_string(), Num(123e4)),
-                        ("string".to_string(), Str(" abc 123 ".to_string())),
+                        ("null".to_owned(), Null),
+                        ("true".to_owned(), Boolean(true)),
+                        ("false".to_owned(), Boolean(false)),
+                        ("number".to_owned(), Num(123e4)),
+                        ("string".to_owned(), Str(" abc 123 ".to_owned())),
                         (
-                            "array".to_string(),
-                            Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+                            "array".to_owned(),
+                            Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())])
                         ),
                         (
-                            "object".to_string(),
+                            "object".to_owned(),
                             Object(
                                 vec![
-                                    ("a".to_string(), Num(1.0)),
-                                    ("b".to_string(), Str("c".to_string())),
+                                    ("a".to_owned(), Num(1.0)),
+                                    ("b".to_owned(), Str("c".to_owned())),
                                 ]
                                 .into_iter()
                                 .collect()
                             )
                         ),
-                        ("empty_array".to_string(), Array(vec![]),),
-                        ("empty_object".to_string(), Object(HashMap::new()),),
+                        ("empty_array".to_owned(), Array(vec![]),),
+                        ("empty_object".to_owned(), Object(HashMap::new()),),
                     ]
                     .into_iter()
                     .collect()
diff --git a/crates/winnow/examples/json/parser_partial.rs b/crates/winnow/examples/json/parser_partial.rs
index 31aba00..864f7b2 100644
--- a/crates/winnow/examples/json/parser_partial.rs
+++ b/crates/winnow/examples/json/parser_partial.rs
@@ -5,22 +5,22 @@
 use winnow::{
     ascii::float,
     combinator::alt,
-    combinator::{cut_err, rest},
+    combinator::cut_err,
     combinator::{delimited, preceded, separated_pair, terminated},
     combinator::{repeat, separated},
-    error::{AddContext, ParserError},
+    error::{AddContext, ParserError, StrContext},
     stream::Partial,
-    token::{any, none_of, take, take_while},
+    token::{any, none_of, rest, take, take_while},
 };
 
 use crate::json::JsonValue;
 
-pub type Stream<'i> = Partial<&'i str>;
+pub(crate) type Stream<'i> = Partial<&'i str>;
 
 /// The root element of a JSON parser is any value
 ///
 /// A parser has the following signature:
-/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
+/// `&mut Stream -> PResult<Output, ContextError>`, with `PResult` defined as:
 /// `type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;`
 ///
 /// most of the times you can ignore the error type and use the default (but this
@@ -29,7 +29,7 @@
 /// Here we use `&str` as input type, but parsers can be generic over
 /// the input type, work directly with `&[u8]`, or any other type that
 /// implements the required traits.
-pub fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+pub(crate) fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     delimited(ws, json_value, ws_or_eof).parse_next(input)
@@ -37,7 +37,7 @@
 
 /// `alt` is a combinator that tries multiple parsers one by one, until
 /// one of them succeeds
-fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     // `alt` combines the each value parser. It returns the result of the first
@@ -53,7 +53,7 @@
     .parse_next(input)
 }
 
-/// `tag(string)` generates a parser that recognizes the argument string.
+/// `literal(string)` generates a parser that takes the argument string.
 ///
 /// This also shows returning a sub-slice of the original input
 fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
@@ -76,9 +76,9 @@
     alt((parse_true, parse_false)).parse_next(input)
 }
 
-/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// This parser gathers all `char`s up into a `String`with a parse to take the double quote
 /// character, before the string (using `preceded`) and after the string (using `terminated`).
-fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<String, E> {
     preceded(
@@ -97,7 +97,7 @@
     )
     // `context` lets you add a static string to errors to provide more information in the
     // error chain (to indicate which parser had an error)
-    .context("string")
+    .context(StrContext::Expected("string".into()))
     .parse_next(input)
 }
 
@@ -158,7 +158,7 @@
 /// accumulating results in a `Vec`, until it encounters an error.
 /// If you want more control on the parser application, check out the `iterator`
 /// combinator (cf `examples/iterator.rs`)
-fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<Vec<JsonValue>, E> {
     preceded(
@@ -168,11 +168,11 @@
             (ws, ']'),
         )),
     )
-    .context("array")
+    .context(StrContext::Expected("array".into()))
     .parse_next(input)
 }
 
-fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<HashMap<String, JsonValue>, E> {
     preceded(
@@ -182,11 +182,11 @@
             (ws, '}'),
         )),
     )
-    .context("object")
+    .context(StrContext::Expected("object".into()))
     .parse_next(input)
 }
 
-fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<(String, JsonValue), E> {
     separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
@@ -211,54 +211,50 @@
 #[cfg(test)]
 mod test {
     #[allow(clippy::useless_attribute)]
-    #[allow(dead_code)] // its dead for benches
+    #[allow(unused_imports)] // its dead for benches
     use super::*;
 
     #[allow(clippy::useless_attribute)]
     #[allow(dead_code)] // its dead for benches
-    type Error<'i> = winnow::error::InputError<Partial<&'i str>>;
+    type Error = winnow::error::ContextError;
 
     #[test]
     fn json_string() {
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new("\"\"")),
-            Ok((Partial::new(""), "".to_string()))
+            string::<Error>.parse_peek(Partial::new("\"\"")),
+            Ok((Partial::new(""), "".to_owned()))
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new("\"abc\"")),
-            Ok((Partial::new(""), "abc".to_string()))
+            string::<Error>.parse_peek(Partial::new("\"abc\"")),
+            Ok((Partial::new(""), "abc".to_owned()))
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new(
+            string::<Error>.parse_peek(Partial::new(
                 "\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""
             )),
             Ok((
                 Partial::new(""),
-                "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string()
+                "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned()
             )),
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")),
-            Ok((Partial::new(""), "😐".to_string()))
+            string::<Error>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")),
+            Ok((Partial::new(""), "😐".to_owned()))
         );
 
-        assert!(string::<Error<'_>>.parse_peek(Partial::new("\"")).is_err());
-        assert!(string::<Error<'_>>
-            .parse_peek(Partial::new("\"abc"))
-            .is_err());
-        assert!(string::<Error<'_>>
-            .parse_peek(Partial::new("\"\\\""))
-            .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>.parse_peek(Partial::new("\"")).is_err());
+        assert!(string::<Error>.parse_peek(Partial::new("\"abc")).is_err());
+        assert!(string::<Error>.parse_peek(Partial::new("\"\\\"")).is_err());
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\u123\""))
             .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\uD800\""))
             .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\uD800\\uD800\""))
             .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\uDC00\""))
             .is_err());
     }
@@ -271,15 +267,15 @@
 
         let expected = Object(
             vec![
-                ("a".to_string(), Num(42.0)),
-                ("b".to_string(), Str("x".to_string())),
+                ("a".to_owned(), Num(42.0)),
+                ("b".to_owned(), Str("x".to_owned())),
             ]
             .into_iter()
             .collect(),
         );
 
         assert_eq!(
-            json::<Error<'_>>.parse_peek(Partial::new(input)),
+            json::<Error>.parse_peek(Partial::new(input)),
             Ok((Partial::new(""), expected))
         );
     }
@@ -290,10 +286,10 @@
 
         let input = r#"[42,"x"]"#;
 
-        let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+        let expected = Array(vec![Num(42.0), Str("x".to_owned())]);
 
         assert_eq!(
-            json::<Error<'_>>.parse_peek(Partial::new(input)),
+            json::<Error>.parse_peek(Partial::new(input)),
             Ok((Partial::new(""), expected))
         );
     }
@@ -317,33 +313,33 @@
   "#;
 
         assert_eq!(
-            json::<Error<'_>>.parse_peek(Partial::new(input)),
+            json::<Error>.parse_peek(Partial::new(input)),
             Ok((
                 Partial::new(""),
                 Object(
                     vec![
-                        ("null".to_string(), Null),
-                        ("true".to_string(), Boolean(true)),
-                        ("false".to_string(), Boolean(false)),
-                        ("number".to_string(), Num(123e4)),
-                        ("string".to_string(), Str(" abc 123 ".to_string())),
+                        ("null".to_owned(), Null),
+                        ("true".to_owned(), Boolean(true)),
+                        ("false".to_owned(), Boolean(false)),
+                        ("number".to_owned(), Num(123e4)),
+                        ("string".to_owned(), Str(" abc 123 ".to_owned())),
                         (
-                            "array".to_string(),
-                            Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+                            "array".to_owned(),
+                            Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())])
                         ),
                         (
-                            "object".to_string(),
+                            "object".to_owned(),
                             Object(
                                 vec![
-                                    ("a".to_string(), Num(1.0)),
-                                    ("b".to_string(), Str("c".to_string())),
+                                    ("a".to_owned(), Num(1.0)),
+                                    ("b".to_owned(), Str("c".to_owned())),
                                 ]
                                 .into_iter()
                                 .collect()
                             )
                         ),
-                        ("empty_array".to_string(), Array(vec![]),),
-                        ("empty_object".to_string(), Object(HashMap::new()),),
+                        ("empty_array".to_owned(), Array(vec![]),),
+                        ("empty_object".to_owned(), Object(HashMap::new()),),
                     ]
                     .into_iter()
                     .collect()
diff --git a/crates/winnow/examples/json_iterator.rs b/crates/winnow/examples/json_iterator.rs
index 9c21ae3..3ca8677 100644
--- a/crates/winnow/examples/json_iterator.rs
+++ b/crates/winnow/examples/json_iterator.rs
@@ -2,7 +2,7 @@
 
 use winnow::prelude::*;
 use winnow::{
-    ascii::{alphanumeric1 as alphanumeric, escaped, float},
+    ascii::{alphanumeric1 as alphanumeric, float, take_escaped},
     combinator::alt,
     combinator::cut_err,
     combinator::separated,
@@ -11,7 +11,7 @@
     error::StrContext,
     stream::Offset,
     token::one_of,
-    token::{tag, take_while},
+    token::{literal, take_while},
 };
 
 use std::cell::Cell;
@@ -43,7 +43,7 @@
         match string(&mut data) {
             Ok(s) => {
                 self.offset(data);
-                println!("-> {}", s);
+                println!("-> {s}");
                 Some(s)
             }
             _ => None,
@@ -56,7 +56,7 @@
         match boolean(&mut data) {
             Ok(o) => {
                 self.offset(data);
-                println!("-> {}", o);
+                println!("-> {o}");
                 Some(o)
             }
             _ => None,
@@ -69,7 +69,7 @@
         match float::<_, _, ()>.parse_next(&mut data) {
             Ok(o) => {
                 self.offset(data);
-                println!("-> {}", o);
+                println!("-> {o}");
                 Some(o)
             }
             _ => None,
@@ -80,14 +80,14 @@
         println!("array()");
 
         let mut data = self.data();
-        match tag::<_, _, ()>("[").parse_next(&mut data) {
+        match literal::<_, _, ()>("[").parse_next(&mut data) {
             Err(_) => None,
             Ok(_) => {
                 println!("[");
                 self.offset(data);
                 let mut first = true;
                 let mut done = false;
-                let mut previous = std::usize::MAX;
+                let mut previous = usize::MAX;
 
                 let v = self.clone();
 
@@ -104,7 +104,7 @@
                         }
                     }
 
-                    if tag::<_, _, ()>("]").parse_next(&mut data).is_ok() {
+                    if literal::<_, _, ()>("]").parse_next(&mut data).is_ok() {
                         println!("]");
                         v.offset(data);
                         done = true;
@@ -114,7 +114,7 @@
                     if first {
                         first = false;
                     } else {
-                        match tag::<_, _, ()>(",").parse_next(&mut data) {
+                        match literal::<_, _, ()>(",").parse_next(&mut data) {
                             Ok(_) => {
                                 println!(",");
                                 v.offset(data);
@@ -137,7 +137,7 @@
     pub fn object(&self) -> Option<impl Iterator<Item = (&'a str, JsonValue<'a, 'b>)>> {
         println!("object()");
         let mut data = self.data();
-        match tag::<_, _, ()>("{").parse_next(&mut data) {
+        match literal::<_, _, ()>("{").parse_next(&mut data) {
             Err(_) => None,
             Ok(_) => {
                 self.offset(data);
@@ -146,7 +146,7 @@
 
                 let mut first = true;
                 let mut done = false;
-                let mut previous = std::usize::MAX;
+                let mut previous = usize::MAX;
 
                 let v = self.clone();
 
@@ -163,7 +163,7 @@
                         }
                     }
 
-                    if tag::<_, _, ()>("}").parse_next(&mut data).is_ok() {
+                    if literal::<_, _, ()>("}").parse_next(&mut data).is_ok() {
                         println!("}}");
                         v.offset(data);
                         done = true;
@@ -173,7 +173,7 @@
                     if first {
                         first = false;
                     } else {
-                        match tag::<_, _, ()>(",").parse_next(&mut data) {
+                        match literal::<_, _, ()>(",").parse_next(&mut data) {
                             Ok(_) => {
                                 println!(",");
                                 v.offset(data);
@@ -189,7 +189,7 @@
                         Ok(key) => {
                             v.offset(data);
 
-                            match tag::<_, _, ()>(":").parse_next(&mut data) {
+                            match literal::<_, _, ()>(":").parse_next(&mut data) {
                                 Err(_) => None,
                                 Ok(_) => {
                                     v.offset(data);
@@ -216,7 +216,7 @@
 }
 
 fn parse_str<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> {
-    escaped(alphanumeric, '\\', one_of(['"', 'n', '\\'])).parse_next(i)
+    take_escaped(alphanumeric, '\\', one_of(['"', 'n', '\\'])).parse_next(i)
 }
 
 fn string<'s>(i: &mut &'s str) -> PResult<&'s str> {
@@ -276,7 +276,7 @@
 ///
 /// JsonValue.string -> iterator over String (returns None after first successful call)
 ///
-/// object(input).filter(|(k, _)| k == "users").flatten(|(_, v)| v.object()).filter(|(k, _)| k == "city").flatten(|(_,v)| v.string())
+/// object(input).filter(|(k, _)| k == "users").flatten(|(_, v)| v.object()).filter(|(k, _)| k == "city").flatten(|(_,v)| `v.string()`)
 fn main() {
     /*let data = "{
     \"users\": {
@@ -305,7 +305,7 @@
                 })
                 .collect();
 
-            println!("res = {:?}", s);
+            println!("res = {s:?}");
         }
     };
 }
diff --git a/crates/winnow/examples/ndjson/main.rs b/crates/winnow/examples/ndjson/main.rs
index 6b2a716..750a10c 100644
--- a/crates/winnow/examples/ndjson/main.rs
+++ b/crates/winnow/examples/ndjson/main.rs
@@ -2,11 +2,12 @@
 
 use std::io::Read;
 
+use winnow::error::ContextError;
 use winnow::error::ErrMode;
-use winnow::error::InputError;
 use winnow::error::Needed;
 use winnow::prelude::*;
 use winnow::stream::Offset;
+use winnow::stream::Stream as _;
 
 fn main() -> Result<(), lexopt::Error> {
     let args = Args::parse()?;
@@ -14,7 +15,7 @@
         option: Some("<PATH>".to_owned()),
     })?;
 
-    let mut file = std::fs::File::open(&input).map_err(to_lexopt)?;
+    let mut file = std::fs::File::open(input).map_err(to_lexopt)?;
 
     // Intentionally starting with a small buffer to make it easier to show `Incomplete` handling
     let buffer_size = 10;
@@ -23,7 +24,7 @@
     let mut buffer = circular::Buffer::with_capacity(buffer_size);
     loop {
         let read = file.read(buffer.space()).map_err(to_lexopt)?;
-        eprintln!("read {}", read);
+        eprintln!("read {read}");
         if read == 0 {
             // Should be EOF since we always make sure there is `available_space`
             assert_ne!(buffer.available_space(), 0);
@@ -38,13 +39,15 @@
         buffer.fill(read);
 
         loop {
-            let input = parser::Stream::new(std::str::from_utf8(buffer.data()).map_err(to_lexopt)?);
-            match parser::ndjson::<InputError<parser::Stream>>.parse_peek(input) {
-                Ok((remainder, value)) => {
-                    println!("{:?}", value);
+            let mut input =
+                parser::Stream::new(std::str::from_utf8(buffer.data()).map_err(to_lexopt)?);
+            let start = input.checkpoint();
+            match parser::ndjson::<ContextError>.parse_next(&mut input) {
+                Ok(value) => {
+                    println!("{value:?}");
                     println!();
                     // Tell the buffer how much we read
-                    let consumed = remainder.offset_from(&input);
+                    let consumed = input.offset_from(&start);
                     buffer.consume(consumed);
                 }
                 Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => {
@@ -60,7 +63,7 @@
                     // one byte at a time
                     let head_room = size.get().max(min_buffer_growth);
                     let new_capacity = buffer.available_data() + head_room;
-                    eprintln!("growing buffer to {}", new_capacity);
+                    eprintln!("growing buffer to {new_capacity}");
                     buffer.grow(new_capacity);
                     if buffer.available_space() < head_room {
                         eprintln!("buffer shift");
@@ -70,7 +73,7 @@
                 }
                 Err(ErrMode::Incomplete(Needed::Unknown)) => {
                     let new_capacity = buffer_growth_factor * buffer.capacity();
-                    eprintln!("growing buffer to {}", new_capacity);
+                    eprintln!("growing buffer to {new_capacity}");
                     buffer.grow(new_capacity);
                     break;
                 }
diff --git a/crates/winnow/examples/ndjson/parser.rs b/crates/winnow/examples/ndjson/parser.rs
index 101391e..4d5f495 100644
--- a/crates/winnow/examples/ndjson/parser.rs
+++ b/crates/winnow/examples/ndjson/parser.rs
@@ -9,13 +9,13 @@
     combinator::cut_err,
     combinator::{delimited, preceded, separated_pair, terminated},
     combinator::{repeat, separated},
-    error::{AddContext, ParserError},
+    error::{AddContext, ParserError, StrContext},
     stream::Partial,
     token::{any, none_of, take, take_while},
 };
 
 #[derive(Debug, PartialEq, Clone)]
-pub enum JsonValue {
+pub(crate) enum JsonValue {
     Null,
     Boolean(bool),
     Str(String),
@@ -25,9 +25,9 @@
 }
 
 /// Use `Partial` to cause `ErrMode::Incomplete` while parsing
-pub type Stream<'i> = Partial<&'i str>;
+pub(crate) type Stream<'i> = Partial<&'i str>;
 
-pub fn ndjson<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+pub(crate) fn ndjson<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<Option<JsonValue>, E> {
     alt((
@@ -41,7 +41,7 @@
 
 /// `alt` is a combinator that tries multiple parsers one by one, until
 /// one of them succeeds
-fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<JsonValue, E> {
     // `alt` combines the each value parser. It returns the result of the first
@@ -57,7 +57,7 @@
     .parse_next(input)
 }
 
-/// `tag(string)` generates a parser that recognizes the argument string.
+/// `literal(string)` generates a parser that takes the argument string.
 ///
 /// This also shows returning a sub-slice of the original input
 fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
@@ -80,9 +80,9 @@
     alt((parse_true, parse_false)).parse_next(input)
 }
 
-/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// This parser gathers all `char`s up into a `String`with a parse to take the double quote
 /// character, before the string (using `preceded`) and after the string (using `terminated`).
-fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<String, E> {
     preceded(
@@ -101,7 +101,7 @@
     )
     // `context` lets you add a static string to errors to provide more information in the
     // error chain (to indicate which parser had an error)
-    .context("string")
+    .context(StrContext::Expected("string".into()))
     .parse_next(input)
 }
 
@@ -162,7 +162,7 @@
 /// accumulating results in a `Vec`, until it encounters an error.
 /// If you want more control on the parser application, check out the `iterator`
 /// combinator (cf `examples/iterator.rs`)
-fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<Vec<JsonValue>, E> {
     preceded(
@@ -172,11 +172,11 @@
             (ws, ']'),
         )),
     )
-    .context("array")
+    .context(StrContext::Expected("array".into()))
     .parse_next(input)
 }
 
-fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<HashMap<String, JsonValue>, E> {
     preceded(
@@ -186,11 +186,11 @@
             (ws, '}'),
         )),
     )
-    .context("object")
+    .context(StrContext::Expected("object".into()))
     .parse_next(input)
 }
 
-fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, StrContext>>(
     input: &mut Stream<'i>,
 ) -> PResult<(String, JsonValue), E> {
     separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
@@ -210,54 +210,50 @@
 #[cfg(test)]
 mod test {
     #[allow(clippy::useless_attribute)]
-    #[allow(dead_code)] // its dead for benches
+    #[allow(unused_imports)] // its dead for benches
     use super::*;
 
     #[allow(clippy::useless_attribute)]
     #[allow(dead_code)] // its dead for benches
-    type Error<'i> = winnow::error::InputError<Partial<&'i str>>;
+    type Error = winnow::error::ContextError;
 
     #[test]
     fn json_string() {
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new("\"\"")),
-            Ok((Partial::new(""), "".to_string()))
+            string::<Error>.parse_peek(Partial::new("\"\"")),
+            Ok((Partial::new(""), "".to_owned()))
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new("\"abc\"")),
-            Ok((Partial::new(""), "abc".to_string()))
+            string::<Error>.parse_peek(Partial::new("\"abc\"")),
+            Ok((Partial::new(""), "abc".to_owned()))
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new(
+            string::<Error>.parse_peek(Partial::new(
                 "\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""
             )),
             Ok((
                 Partial::new(""),
-                "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string()
+                "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned()
             )),
         );
         assert_eq!(
-            string::<Error<'_>>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")),
-            Ok((Partial::new(""), "😐".to_string()))
+            string::<Error>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")),
+            Ok((Partial::new(""), "😐".to_owned()))
         );
 
-        assert!(string::<Error<'_>>.parse_peek(Partial::new("\"")).is_err());
-        assert!(string::<Error<'_>>
-            .parse_peek(Partial::new("\"abc"))
-            .is_err());
-        assert!(string::<Error<'_>>
-            .parse_peek(Partial::new("\"\\\""))
-            .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>.parse_peek(Partial::new("\"")).is_err());
+        assert!(string::<Error>.parse_peek(Partial::new("\"abc")).is_err());
+        assert!(string::<Error>.parse_peek(Partial::new("\"\\\"")).is_err());
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\u123\""))
             .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\uD800\""))
             .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\uD800\\uD800\""))
             .is_err());
-        assert!(string::<Error<'_>>
+        assert!(string::<Error>
             .parse_peek(Partial::new("\"\\uDC00\""))
             .is_err());
     }
@@ -271,15 +267,15 @@
 
         let expected = Object(
             vec![
-                ("a".to_string(), Num(42.0)),
-                ("b".to_string(), Str("x".to_string())),
+                ("a".to_owned(), Num(42.0)),
+                ("b".to_owned(), Str("x".to_owned())),
             ]
             .into_iter()
             .collect(),
         );
 
         assert_eq!(
-            ndjson::<Error<'_>>.parse_peek(Partial::new(input)),
+            ndjson::<Error>.parse_peek(Partial::new(input)),
             Ok((Partial::new(""), Some(expected)))
         );
     }
@@ -291,10 +287,10 @@
         let input = r#"[42,"x"]
 "#;
 
-        let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+        let expected = Array(vec![Num(42.0), Str("x".to_owned())]);
 
         assert_eq!(
-            ndjson::<Error<'_>>.parse_peek(Partial::new(input)),
+            ndjson::<Error>.parse_peek(Partial::new(input)),
             Ok((Partial::new(""), Some(expected)))
         );
     }
@@ -307,33 +303,33 @@
 "#;
 
         assert_eq!(
-            ndjson::<Error<'_>>.parse_peek(Partial::new(input)),
+            ndjson::<Error>.parse_peek(Partial::new(input)),
             Ok((
                 Partial::new(""),
                 Some(Object(
                     vec![
-                        ("null".to_string(), Null),
-                        ("true".to_string(), Boolean(true)),
-                        ("false".to_string(), Boolean(false)),
-                        ("number".to_string(), Num(123e4)),
-                        ("string".to_string(), Str(" abc 123 ".to_string())),
+                        ("null".to_owned(), Null),
+                        ("true".to_owned(), Boolean(true)),
+                        ("false".to_owned(), Boolean(false)),
+                        ("number".to_owned(), Num(123e4)),
+                        ("string".to_owned(), Str(" abc 123 ".to_owned())),
                         (
-                            "array".to_string(),
-                            Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+                            "array".to_owned(),
+                            Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())])
                         ),
                         (
-                            "object".to_string(),
+                            "object".to_owned(),
                             Object(
                                 vec![
-                                    ("a".to_string(), Num(1.0)),
-                                    ("b".to_string(), Str("c".to_string())),
+                                    ("a".to_owned(), Num(1.0)),
+                                    ("b".to_owned(), Str("c".to_owned())),
                                 ]
                                 .into_iter()
                                 .collect()
                             )
                         ),
-                        ("empty_array".to_string(), Array(vec![]),),
-                        ("empty_object".to_string(), Object(HashMap::new()),),
+                        ("empty_array".to_owned(), Array(vec![]),),
+                        ("empty_object".to_owned(), Object(HashMap::new()),),
                     ]
                     .into_iter()
                     .collect()
diff --git a/crates/winnow/examples/s_expression/parser.rs b/crates/winnow/examples/s_expression/parser.rs
index c445053..abcf5a4 100644
--- a/crates/winnow/examples/s_expression/parser.rs
+++ b/crates/winnow/examples/s_expression/parser.rs
@@ -16,16 +16,16 @@
 
 /// We start with a top-level function to tie everything together, letting
 /// us call eval on a string directly
-pub fn eval_from_str(src: &str) -> Result<Expr, String> {
+pub(crate) fn eval_from_str(src: &str) -> Result<Expr, String> {
     parse_expr
         .parse(src)
         .map_err(|e| e.to_string())
-        .and_then(|exp| eval_expression(exp).ok_or_else(|| "Eval failed".to_string()))
+        .and_then(|exp| eval_expression(exp).ok_or_else(|| "Eval failed".to_owned()))
 }
 
 /// For parsing, we start by defining the types that define the shape of data that we want.
 /// In this case, we want something tree-like
-
+///
 /// The remaining half is Lists. We implement these as recursive Expressions.
 /// For a list of numbers, we have `'(1 2 3)`, which we'll parse to:
 /// ```
@@ -36,7 +36,7 @@
 /// structure that we can deal with programmatically. Thus any valid expression
 /// is also a valid data structure in Lisp itself.
 #[derive(Debug, Eq, PartialEq, Clone)]
-pub enum Expr {
+pub(crate) enum Expr {
     Constant(Atom),
     /// (func-name arg1 arg2)
     Application(Box<Expr>, Vec<Expr>),
@@ -51,7 +51,7 @@
 /// We now wrap this type and a few other primitives into our Atom type.
 /// Remember from before that Atoms form one half of our language.
 #[derive(Debug, Eq, PartialEq, Clone)]
-pub enum Atom {
+pub(crate) enum Atom {
     Num(i32),
     Keyword(String),
     Boolean(bool),
@@ -60,7 +60,7 @@
 
 /// Now, the most basic type. We define some built-in functions that our lisp has
 #[derive(Debug, Eq, PartialEq, Clone, Copy)]
-pub enum BuiltIn {
+pub(crate) enum BuiltIn {
     Plus,
     Minus,
     Times,
@@ -153,7 +153,7 @@
 fn parse_keyword(i: &mut &'_ str) -> PResult<Atom> {
     preceded(":", cut_err(alpha1))
         .context(StrContext::Label("keyword"))
-        .map(|sym_str: &str| Atom::Keyword(sym_str.to_string()))
+        .map(|sym_str: &str| Atom::Keyword(sym_str.to_owned()))
         .parse_next(i)
 }
 
@@ -242,7 +242,7 @@
 /// a little interpreter to take an Expr, which is really an
 /// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST),
 /// and give us something back
-
+///
 /// This function tries to reduce the AST.
 /// This has to return an Expression rather than an Atom because quoted `s_expressions`
 /// can't be reduced
diff --git a/crates/winnow/examples/string/main.rs b/crates/winnow/examples/string/main.rs
index 0c2647e..3f57f40 100644
--- a/crates/winnow/examples/string/main.rs
+++ b/crates/winnow/examples/string/main.rs
@@ -21,8 +21,8 @@
     let data = args.input.as_deref().unwrap_or("\"abc\"");
     let result = parser::parse_string::<()>.parse(data);
     match result {
-        Ok(data) => println!("{}", data),
-        Err(err) => println!("{:?}", err),
+        Ok(data) => println!("{data}"),
+        Err(err) => println!("{err:?}"),
     }
 
     Ok(())
diff --git a/crates/winnow/examples/string/parser.rs b/crates/winnow/examples/string/parser.rs
index 7701335..a84df74 100644
--- a/crates/winnow/examples/string/parser.rs
+++ b/crates/winnow/examples/string/parser.rs
@@ -19,7 +19,7 @@
 
 /// Parse a string. Use a loop of `parse_fragment` and push all of the fragments
 /// into an output string.
-pub fn parse_string<'a, E>(input: &mut &'a str) -> PResult<String, E>
+pub(crate) fn parse_string<'a, E>(input: &mut &'a str) -> PResult<String, E>
 where
     E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
 {
diff --git a/crates/winnow/src/_topic/error.rs b/crates/winnow/src/_topic/error.rs
index 8a401b4..35caa9d 100644
--- a/crates/winnow/src/_topic/error.rs
+++ b/crates/winnow/src/_topic/error.rs
@@ -1,18 +1,33 @@
 //! # Custom Errors
 //!
-//! Between [`ContextError`], [`Parser::context`], and [`cut_err`],
-//! most error needs will likely be met
-//! (see [tutorial][chapter_6]).
-//! When that isn't the case, you can implement your own error type.
+//! A lot can be accomplished with the built-in error tools, like:
+//! - [`ContextError`]
+//! - [`Parser::context`]
+//! - [`cut_err`]
 //!
-//! The most basic error trait is [`ParserError`].
+//! *(see [tutorial][chapter_7])*
+//!
+//! Most other needs can likely be met by using a custom context type with [`ContextError`] instead
+//! of [`StrContext`].
+//! This will require implementing a custom renderer.
+//!
+//! ## `ParserError` Trait
+//!
+//! When needed, you can also create your own type that implements [`ParserError`].
 //!
 //! Optional traits include:
 //! - [`AddContext`]
 //! - [`FromExternalError`]
+//! - [`ErrorConvert`]
 //!
-//! # Example
+//! There are multiple strategies for implementing support for [`AddContext`] and [`FromExternalError`]:
+//! - Make your error type generic over the context or external error
+//! - Require a trait for the context or external error and `Box` it
+//! - Make the context an enum like [`StrContext`]
+//! - Implement the trait multiple times, one for each concrete context or external error type,
+//!   allowing custom behavior per type
 //!
+//! Example:
 //!```rust
 #![doc = include_str!("../../examples/custom_error.rs")]
 //!```
@@ -20,8 +35,10 @@
 #![allow(unused_imports)]
 use crate::combinator::cut_err;
 use crate::error::ContextError;
+use crate::error::ErrorConvert;
+use crate::error::StrContext;
 use crate::Parser;
-use crate::_tutorial::chapter_6;
+use crate::_tutorial::chapter_7;
 use crate::error::AddContext;
 use crate::error::FromExternalError;
 use crate::error::ParserError;
diff --git a/crates/winnow/src/_topic/language.rs b/crates/winnow/src/_topic/language.rs
index c6a713a..740fbe7 100644
--- a/crates/winnow/src/_topic/language.rs
+++ b/crates/winnow/src/_topic/language.rs
@@ -62,12 +62,12 @@
 //! use winnow::prelude::*;
 //! use winnow::{
 //!   error::ParserError,
-//!   token::take_till1,
+//!   token::take_till,
 //! };
 //!
 //! pub fn peol_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E>
 //! {
-//!   ('%', take_till1(['\n', '\r']))
+//!   ('%', take_till(1.., ['\n', '\r']))
 //!     .void() // Output is thrown away.
 //!     .parse_next(i)
 //! }
@@ -75,14 +75,14 @@
 //!
 //! ### `/* C-style comments */`
 //!
-//! Inline comments surrounded with sentinel tags `(*` and `*)`. This version returns an output of `()`
+//! Inline comments surrounded with sentinel literals `(*` and `*)`. This version returns an output of `()`
 //! and does not handle nested comments.
 //!
 //! ```rust
 //! use winnow::prelude::*;
 //! use winnow::{
 //!   error::ParserError,
-//!   token::{tag, take_until},
+//!   token::take_until,
 //! };
 //!
 //! pub fn pinline_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> {
@@ -116,16 +116,16 @@
 //!       one_of(|c: char| c.is_alpha() || c == '_'),
 //!       take_while(0.., |c: char| c.is_alphanum() || c == '_')
 //!   )
-//!   .recognize()
+//!   .take()
 //!   .parse_next(input)
 //! }
 //! ```
 //!
 //! Let's say we apply this to the identifier `hello_world123abc`. The first element of the tuple
-//! would uses [`one_of`][crate::token::one_of] which would recognize `h`. The tuple ensures that
+//! would uses [`one_of`][crate::token::one_of] which would take `h`. The tuple ensures that
 //! `ello_world123abc` will be piped to the next [`take_while`][crate::token::take_while] parser,
-//! which recognizes every remaining character. However, the tuple returns a tuple of the results
-//! of its sub-parsers. The [`recognize`][crate::Parser::recognize] parser produces a `&str` of the
+//! which takes every remaining character. However, the tuple returns a tuple of the results
+//! of its sub-parsers. The [`take`][crate::Parser::take] parser produces a `&str` of the
 //! input text that was parsed, which in this case is the entire `&str` `hello_world123abc`.
 //!
 //! ## Literal Values
@@ -136,7 +136,7 @@
 #![doc = include_str!("../../examples/string/parser.rs")]
 //! ```
 //!
-//! See also [`escaped`] and [`escaped_transform`].
+//! See also [`take_escaped`] and [`escaped_transform`].
 //!
 //! ### Integers
 //!
@@ -159,7 +159,6 @@
 //!   combinator::{repeat},
 //!   combinator::{preceded, terminated},
 //!   token::one_of,
-//!   token::tag,
 //! };
 //!
 //! fn hexadecimal<'s>(input: &mut &'s str) -> PResult<&'s str> { // <'a, E: ParserError<&'a str>>
@@ -167,7 +166,7 @@
 //!     alt(("0x", "0X")),
 //!     repeat(1..,
 //!       terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ()))
-//!     ).map(|()| ()).recognize()
+//!     ).map(|()| ()).take()
 //!   ).parse_next(input)
 //! }
 //! ```
@@ -181,7 +180,6 @@
 //!   combinator::{repeat},
 //!   combinator::{preceded, terminated},
 //!   token::one_of,
-//!   token::tag,
 //! };
 //!
 //! fn hexadecimal_value(input: &mut &str) -> PResult<i64> {
@@ -189,7 +187,7 @@
 //!     alt(("0x", "0X")),
 //!     repeat(1..,
 //!       terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ()))
-//!     ).map(|()| ()).recognize()
+//!     ).map(|()| ()).take()
 //!   ).try_map(
 //!     |out: &str| i64::from_str_radix(&str::replace(&out, "_", ""), 16)
 //!   ).parse_next(input)
@@ -207,7 +205,6 @@
 //!   combinator::{repeat},
 //!   combinator::{preceded, terminated},
 //!   token::one_of,
-//!   token::tag,
 //! };
 //!
 //! fn octal<'s>(input: &mut &'s str) -> PResult<&'s str> {
@@ -215,7 +212,7 @@
 //!     alt(("0o", "0O")),
 //!     repeat(1..,
 //!       terminated(one_of('0'..='7'), repeat(0.., '_').map(|()| ()))
-//!     ).map(|()| ()).recognize()
+//!     ).map(|()| ()).take()
 //!   ).parse_next(input)
 //! }
 //! ```
@@ -229,7 +226,6 @@
 //!   combinator::{repeat},
 //!   combinator::{preceded, terminated},
 //!   token::one_of,
-//!   token::tag,
 //! };
 //!
 //! fn binary<'s>(input: &mut &'s str) -> PResult<&'s str> {
@@ -237,7 +233,7 @@
 //!     alt(("0b", "0B")),
 //!     repeat(1..,
 //!       terminated(one_of('0'..='1'), repeat(0.., '_').map(|()| ()))
-//!     ).map(|()| ()).recognize()
+//!     ).map(|()| ()).take()
 //!   ).parse_next(input)
 //! }
 //! ```
@@ -256,7 +252,7 @@
 //!   repeat(1..,
 //!     terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ()))
 //!   ).map(|()| ())
-//!     .recognize()
+//!     .take()
 //!     .parse_next(input)
 //! }
 //! ```
@@ -288,7 +284,7 @@
 //!         opt(one_of(['+', '-'])),
 //!         decimal
 //!       ))
-//!     ).recognize()
+//!     ).take()
 //!     , // Case two: 42e42 and 42.42e42
 //!     (
 //!       decimal,
@@ -299,13 +295,13 @@
 //!       one_of(['e', 'E']),
 //!       opt(one_of(['+', '-'])),
 //!       decimal
-//!     ).recognize()
+//!     ).take()
 //!     , // Case three: 42. and 42.42
 //!     (
 //!       decimal,
 //!       '.',
 //!       opt(decimal)
-//!     ).recognize()
+//!     ).take()
 //!   )).parse_next(input)
 //! }
 //!
@@ -314,7 +310,7 @@
 //!     terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ()))
 //!   ).
 //!   map(|()| ())
-//!     .recognize()
+//!     .take()
 //!     .parse_next(input)
 //! }
 //! ```
@@ -324,7 +320,7 @@
 #![allow(unused_imports)]
 use crate::ascii::dec_int;
 use crate::ascii::dec_uint;
-use crate::ascii::escaped;
 use crate::ascii::escaped_transform;
 use crate::ascii::float;
 use crate::ascii::hex_uint;
+use crate::ascii::take_escaped;
diff --git a/crates/winnow/src/_topic/mod.rs b/crates/winnow/src/_topic/mod.rs
index c7bcc53..f279ac6 100644
--- a/crates/winnow/src/_topic/mod.rs
+++ b/crates/winnow/src/_topic/mod.rs
@@ -3,7 +3,7 @@
 //! These are short recipes for accomplishing common tasks.
 //!
 //! - [Why `winnow`?][why]
-//! - [Migrating from `nom`][nom]
+//! - [For `nom` users][nom]
 //! - Formats:
 //!   - [Elements of Programming Languages][language]
 //!   - [Arithmetic][arithmetic]
diff --git a/crates/winnow/src/_topic/nom.rs b/crates/winnow/src/_topic/nom.rs
index f3cb74e..4c9c45f 100644
--- a/crates/winnow/src/_topic/nom.rs
+++ b/crates/winnow/src/_topic/nom.rs
@@ -1,4 +1,6 @@
-//! # Migrating from `nom`
+//! # For `nom` users
+//!
+//! ## Migrating from `nom`
 //!
 //! For comparisons with `nom`, see
 //! - [Why `winnow`][super::why]
@@ -10,6 +12,8 @@
 //! of the `nom` combinator.  It is expected that, where names diverge, a doc alias exists.
 //! See also the [List of combinators][crate::combinator].
 //!
+//! ### Complex migrations
+//!
 //! For larger parsers, it is likely best to take smaller steps
 //! - Easier to debug when something goes wrong
 //! - Deprecation messages will help assist through the process
@@ -17,23 +21,25 @@
 //! The workflow goes something like:
 //! 1. Run `cargo rm nom && cargo add [email protected]`
 //! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [migration
-//!    notes](https://github.com/winnow-rs/winnow/blob/v0.3-main/CHANGELOG.md#nom-migration-guide))
+//!    notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#nom-migration-guide))
 //! 1. Commit
 //! 1. Switch any `impl FnMut(I) -> IResult<I, O, E>` to `impl Parser<I, O, E>`
 //! 1. Resolve deprecation messages
 //! 1. Commit
 //! 1. Run `cargo add [email protected]`
-//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [changelog](https://github.com/winnow-rs/winnow/blob/v0.4-main/CHANGELOG.md#compatibility-2) for more details)
+//! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#040---2023-03-18) for more details)
 //! 1. Commit
 //! 1. Resolve deprecation messages
 //! 1. Commit
 //! 1. Run `cargo add [email protected]`
 //! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [migration
-//!     notes](https://github.com/winnow-rs/winnow/blob/v0.5.0/CHANGELOG.md))
+//!     notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#050---2023-07-13))
 //! 1. Commit
-//! 1. Resolve deprecation messagess
+//! 1. Resolve deprecation messages
 //! 1. Commit
 //!
+//! ### Examples
+//!
 //! For example migrations, see
 //! - [git-config-env](https://github.com/gitext-rs/git-config-env/pull/11) (nom to winnow 0.3)
 //! - [git-conventional](https://github.com/crate-ci/git-conventional/pull/37) (nom to winnow 0.3,
@@ -47,3 +53,52 @@
 //!   to winnow 0.5)
 //! - [gitoxide](https://github.com/Byron/gitoxide/pull/956) (gradual migration from nom
 //!   to winnow 0.5)
+//!
+//! ## API differences
+//!
+//! ### Renamed APIs
+//!
+//! Names have changed for consistency or clarity.
+//!
+//! To find a parser you are looking for,
+//! - Search the docs for the `nom` parser
+//! - See the [List of combinators][crate::combinator]
+//!
+//! ### `&mut I`
+//!
+//! For an explanation of this change, see [Why `winnow`][super::why]
+//!
+//! To save and restore from intermediate states, [`Stream::checkpoint`] and [`Stream::reset`] can help:
+//! ```rust
+//! use winnow::stream::Stream as _;
+//! # let mut i = "";
+//! # let i = &mut i;
+//!
+//! let start = i.checkpoint();
+//! // ...
+//! i.reset(&start);
+//! ```
+//!
+//! When the Output of a parser is a slice, you have to add a lifetime:
+//! ```rust
+//! # use winnow::prelude::*;
+//! fn foo<'i>(i: &mut &'i str) -> PResult<&'i str> {
+//!     // ...
+//! #   winnow::token::rest.parse_next(i)
+//! }
+//! ```
+//!
+//! When writing a closure, you need to annotate the type:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::combinator::trace;
+//! fn foo(i: &mut &str) -> PResult<usize> {
+//!     trace("foo", |i: &mut _| {
+//!         // ...
+//! #       Ok(0)
+//!     }).parse_next(i)
+//! }
+//! ```
+
+#![allow(unused_imports)]
+use crate::stream::Stream;
diff --git a/crates/winnow/src/_topic/stream.rs b/crates/winnow/src/_topic/stream.rs
index 2254f87..eb9573e 100644
--- a/crates/winnow/src/_topic/stream.rs
+++ b/crates/winnow/src/_topic/stream.rs
@@ -5,7 +5,7 @@
 //! - Improved debug output like [`Bytes`]
 //! - [`Stateful`] for passing state through your parser, like tracking recursion
 //!   depth
-//! - [`Located`] for looking up the absolute position of a token
+//! - [`LocatingSlice`] for looking up the absolute position of a token
 //!
 //! But that won't always cut it for your parser. For example, you might lex `&str` into
 //! a series of tokens and then want to parse a `TokenStream`.
@@ -18,7 +18,6 @@
 //! The goal is to define parsers with this signature: `&mut MyStream -> PResult<Output>`.
 //! ```rust
 //! # use winnow::prelude::*;
-//! # use winnow::token::tag;
 //! # type MyStream<'i> = &'i str;
 //! # type Output<'i> = &'i str;
 //! fn parser<'s>(i: &mut MyStream<'s>) -> PResult<Output<'s>> {
diff --git a/crates/winnow/src/_topic/why.rs b/crates/winnow/src/_topic/why.rs
index fc1716a..625c5b5 100644
--- a/crates/winnow/src/_topic/why.rs
+++ b/crates/winnow/src/_topic/why.rs
@@ -2,10 +2,14 @@
 //!
 //! To answer this question, it will be useful to contrast this with other approaches to parsing.
 //!
+//! <div class="warning">
+//!
 //! **Note:** This will focus on principles and priorities. For a deeper and wider wider
 //! comparison with other Rust parser libraries, see
 //! [parse-rosetta-rs](https://github.com/rosetta-rs/parse-rosetta-rs).
 //!
+//! </div>
+//!
 //! ## Hand-written parsers
 //!
 //! Typically, a hand-written parser gives you the flexibility to get
@@ -36,18 +40,18 @@
 //!
 //! `winnow` includes support for:
 //! - Zero-copy parsing
-//! - [Parse traces][crate::trace] for easier debugging
-//! - [Streaming parsing][crate::Partial] for network communication or large file
-//! - [Stateful][crate::Stateful] parsers
+//! - [Parse traces][trace] for easier debugging
+//! - [Streaming parsing][Partial] for network communication or large file
+//! - [Stateful] parsers
 //!
 //! For binary formats, `winnow` includes:
-//! - [A hexadecimal view][crate::Bytes] in [traces][crate::trace]
+//! - [A hexadecimal view][crate::Bytes] in [trace]
 //! - [TLV](https://en.wikipedia.org/wiki/Type-length-value) (e.g. [`length_take`])
 //! - Some common parsers to help get started, like numbers
 //!
 //! For text formats, `winnow` includes:
-//! - [Tracking of spans][crate::Located]
-//! - [A textual view when parsing as bytes][crate::BStr] in [traces][crate::trace]
+//! - [Tracking of spans][crate::LocatingSlice]
+//! - [A textual view when parsing as bytes][crate::BStr] in [trace]
 //! - Ability to evaluate directly, parse to an AST, or lex and parse the format
 //!
 //! This works well for:
@@ -70,6 +74,25 @@
 //!
 //! See also our [nom migration guide][super::nom]
 //!
+//! ### Design trade-offs
+//!
+//! `winnow` switched from pure-function parser (`Fn(I) -> (I, O)` to `Fn(&mut I) -> O`).
+//! On error, `i` is left pointing at where the error happened.
+//!
+//! Benefits:
+//! - Cleaner code: Removes need to pass `i` everywhere and makes changes to `i` more explicit
+//! - Correctness: No forgetting to chain `i` through a parser
+//! - Flexibility: `I` does not need to be `Copy` or even `Clone`. For example, [`Stateful`] can use `&mut S` instead of `RefCell<S>`.
+//! - Performance: `Result::Ok` is smaller without `i`, reducing the risk that the output will be
+//!   returned on the stack, rather than the much faster CPU registers.
+//!   `Result::Err` can also be smaller because the error type does not need to carry `i` to point
+//!   to the error.
+//!   See also [#72](https://github.com/winnow-rs/winnow/issues/72).
+//!
+//! Downsides:
+//! - When returning a slice, you have to add a lifetime (`fn foo<'i>(i: &mut &i str) -> PResult<&i str>`)
+//! - When writing a closure, you need to annotate the type (`|i: &mut _|`, at least the full type isn't needed)
+//!
 //! ## `chumsky`
 //!
 //! [`chumsky`](https://crates.io/crates/chumsky) is an up and coming parser-combinator library
@@ -98,4 +121,7 @@
 
 #![allow(unused_imports)]
 use crate::binary::length_take;
+use crate::combinator::trace;
 use crate::stream::Accumulate;
+use crate::stream::Partial;
+use crate::stream::Stateful;
diff --git a/crates/winnow/src/_tutorial/chapter_0.rs b/crates/winnow/src/_tutorial/chapter_0.rs
index 35a2d14..47e85a2 100644
--- a/crates/winnow/src/_tutorial/chapter_0.rs
+++ b/crates/winnow/src/_tutorial/chapter_0.rs
@@ -25,11 +25,10 @@
 //!
 //! Parser combinators are great because:
 //!
-//! - The parsers are small and easy to write
-//! - The parsers components are easy to reuse (if they're general enough, please add them to winnow!)
-//! - The parsers components are easy to test separately (unit tests and property-based tests)
-//! - The parser combination code looks close to the grammar you would have written
-//! - You can build partial parsers, specific to the data you need at the moment, and ignore the rest
+//! - Individual parser functions are small, focused on one thing, ignoring the rest
+//! - You can write tests focused on individual parsers (unit tests and property-based tests)
+//!   in addition to testing the top-level parser as a whole.
+//! - Top-level parsing code looks close to the grammar you would have written
 
 #![allow(unused_imports)]
 use crate::_topic;
diff --git a/crates/winnow/src/_tutorial/chapter_1.rs b/crates/winnow/src/_tutorial/chapter_1.rs
index 2d94418..41220bc 100644
--- a/crates/winnow/src/_tutorial/chapter_1.rs
+++ b/crates/winnow/src/_tutorial/chapter_1.rs
@@ -1,26 +1,25 @@
 //! # Chapter 1: The Winnow Way
 //!
 //! First of all, we need to understand the way that winnow thinks about parsing.
-//! As discussed in the introduction, winnow lets us build simple parsers, and
-//! then combine them (using "combinators").
+//! As discussed in the introduction, winnow lets us compose more complex parsers from more simple
+//! ones (using "combinators").
 //!
-//! Let's discuss what a "parser" actually does. A parser takes an input and returns
+//! Let's discuss what a "parser" actually does. A parser takes an input and advances it until it returns
 //! a result, where:
 //!  - `Ok` indicates the parser successfully found what it was looking for; or
 //!  - `Err` indicates the parser could not find what it was looking for.
 //!
 //! Parsers do more than just return a binary "success"/"failure" code.
-//! On success, the parser will return the processed data. The input will be left pointing to
-//! data that still needs processing
-//!
-//! If the parser failed, then there are multiple errors that could be returned.
-//! For simplicity, however, in the next chapters we will leave these unexplored.
+//! - On success, the parser will return the processed data. The input will be advanced to the end of
+//!   what was processed, pointing to what will be parsed next.
+//! - If the parser failed, then there are multiple errors that could be returned.
+//!   We'll explore this further in [`chapter_7`].
 //!
 //! ```text
-//!                                   ┌─► Ok(what matched the parser)
-//!             ┌─────────┐           │
-//! my input───►│my parser├──►either──┤
-//!             └─────────┘           └─► Err(...)
+//!                                  ┌─► Ok(what matched the parser)
+//!             ┌────────┐           │
+//! my input───►│a parser├──►either──┤
+//!             └────────┘           └─► Err(...)
 //! ```
 //!
 //!
@@ -37,13 +36,14 @@
 //! To combine parsers, we need a common way to refer to them which is where the [`Parser<I, O, E>`]
 //! trait comes in with [`Parser::parse_next`] being the primary way to drive
 //! parsing forward.
+//! In [`chapter_6`], we'll cover how to integrate these into your application, particularly with
+//! [`Parser::parse`].
 //!
 //! You'll note that `I` and `O` are parameterized -- while most of the examples in this book
 //! will be with `&str` (i.e. parsing a string); they do not have to be strings; nor do they
 //! have to be the same type (consider the simple example where `I = &str`, and `O = u64` -- this
 //! parses a string into an unsigned integer.)
 //!
-//!
 //! # Let's write our first parser!
 //!
 //! The simplest parser we can write is one which successfully does nothing.
@@ -54,7 +54,7 @@
 //! This parser function should take in a `&str`:
 //!
 //!  - Since it is supposed to succeed, we know it will return the `Ok` variant.
-//!  - Since it does nothing to our input, the remaining input is the same as the input.
+//!  - Since it does nothing to our input, the input will be left where it started.
 //!  - Since it doesn't parse anything, it also should just return an empty string.
 //!
 //! ```rust
@@ -78,6 +78,8 @@
 //! ```
 
 #![allow(unused_imports)]
+use super::chapter_6;
+use super::chapter_7;
 use crate::PResult;
 use crate::Parser;
 
diff --git a/crates/winnow/src/_tutorial/chapter_2.rs b/crates/winnow/src/_tutorial/chapter_2.rs
index c27b719..6b3953d 100644
--- a/crates/winnow/src/_tutorial/chapter_2.rs
+++ b/crates/winnow/src/_tutorial/chapter_2.rs
@@ -36,14 +36,48 @@
 //! }
 //! ```
 //!
-//! [`any`] and [`Parser::verify`] are [`Parser`] building blocks on top of [`Stream`]:
+//! This extraction of a token is encapsulated in the [`any`] parser:
+//! ```rust
+//! # use winnow::PResult;
+//! # use winnow::error::ParserError;
+//! # use winnow::error::ErrorKind;
+//! # use winnow::error::ErrMode;
+//! use winnow::Parser;
+//! use winnow::token::any;
+//!
+//! fn parse_prefix(input: &mut &str) -> PResult<char> {
+//!     let c = any
+//!         .parse_next(input)?;
+//!     if c != '0' {
+//!         return Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
+//!     }
+//!     Ok(c)
+//! }
+//! #
+//! # fn main()  {
+//! #     let mut input = "0x1a2b Hello";
+//! #
+//! #     let output = parse_prefix.parse_next(&mut input).unwrap();
+//! #
+//! #     assert_eq!(input, "x1a2b Hello");
+//! #     assert_eq!(output, '0');
+//! #
+//! #     assert!(parse_prefix.parse_next(&mut "d").is_err());
+//! # }
+//! ```
+//!
+//! Using the higher level [`any`] parser opens `parse_prefix` to the helpers on the [`Parser`] trait,
+//! like [`Parser::verify`] which fails a parse if a condition isn't met, like our check above:
 //! ```rust
 //! # use winnow::PResult;
 //! use winnow::Parser;
 //! use winnow::token::any;
 //!
 //! fn parse_prefix(input: &mut &str) -> PResult<char> {
-//!     any.verify(|c| *c == '0').parse_next(input)
+//!     let c = any
+//!         .verify(|c| *c == '0')
+//!         .parse_next(input)?;
+//!     Ok(c)
 //! }
 //! #
 //! # fn main()  {
@@ -59,14 +93,14 @@
 //! ```
 //!
 //! Matching a single token literal is common enough that [`Parser`] is implemented for
-//! `char`.
-//!
+//! the `char` type, encapsulating both [`any`] and [`Parser::verify`]:
 //! ```rust
 //! # use winnow::PResult;
 //! use winnow::Parser;
 //!
 //! fn parse_prefix(input: &mut &str) -> PResult<char> {
-//!     '0'.parse_next(input)
+//!     let c = '0'.parse_next(input)?;
+//!     Ok(c)
 //! }
 //! #
 //! # fn main()  {
@@ -115,13 +149,16 @@
 //! }
 //! ```
 //!
-//! Again, matching a literal is common enough that [`Parser`] is implemented for `&str`:
+//! Matching the input position against a string literal is encapsulated in the [`literal`] parser:
 //! ```rust
 //! # use winnow::PResult;
-//! use winnow::Parser;
+//! # use winnow::Parser;
+//! use winnow::token::literal;
 //!
 //! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//!     "0x".parse_next(input)
+//!     let expected = "0x";
+//!     let actual = literal(expected).parse_next(input)?;
+//!     Ok(actual)
 //! }
 //! #
 //! # fn main()  {
@@ -135,12 +172,32 @@
 //! # }
 //! ```
 //!
-//! In `winnow`, we call this type of parser a [`tag`]. See [`token`] for additional individual
-//! and token-slice parsers.
+//! Like for a single token, matching a string literal is common enough that [`Parser`] is implemented for the `&str` type:
+//! ```rust
+//! # use winnow::PResult;
+//! use winnow::Parser;
+//!
+//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//!     let actual = "0x".parse_next(input)?;
+//!     Ok(actual)
+//! }
+//! #
+//! # fn main()  {
+//! #     let mut input = "0x1a2b Hello";
+//! #
+//! #     let output = parse_prefix.parse_next(&mut input).unwrap();
+//! #     assert_eq!(input, "1a2b Hello");
+//! #     assert_eq!(output, "0x");
+//! #
+//! #     assert!(parse_prefix.parse_next(&mut "0o123").is_err());
+//! # }
+//! ```
+//!
+//! See [`token`] for additional individual and token-slice parsers.
 //!
 //! ## Character Classes
 //!
-//! Selecting a single `char` or a [`tag`] is fairly limited. Sometimes, you will want to select one of several
+//! Selecting a single `char` or a [`literal`] is fairly limited. Sometimes, you will want to select one of several
 //! `chars` of a specific class, like digits. For this, we use the [`one_of`] parser:
 //!
 //! ```rust
@@ -237,8 +294,8 @@
 use crate::stream::Stream;
 use crate::token;
 use crate::token::any;
+use crate::token::literal;
 use crate::token::one_of;
-use crate::token::tag;
 use crate::token::take_while;
 use crate::Parser;
 use std::ops::RangeInclusive;
diff --git a/crates/winnow/src/_tutorial/chapter_3.rs b/crates/winnow/src/_tutorial/chapter_3.rs
index 2ad0124..a24f409 100644
--- a/crates/winnow/src/_tutorial/chapter_3.rs
+++ b/crates/winnow/src/_tutorial/chapter_3.rs
@@ -1,9 +1,9 @@
 //! # Chapter 3: Sequencing and Alternatives
 //!
-//! In the last chapter, we saw how to create simple parsers using prebuilt parsers.
+//! In the last chapter, we saw how to create parsers using prebuilt parsers.
 //!
 //! In this chapter, we explore two other widely used features:
-//! alternatives and composition.
+//! sequencing and alternatives.
 //!
 //! ## Sequencing
 //!
@@ -70,7 +70,7 @@
 //! }
 //! ```
 //!
-//! Frequently, you won't care about the tag and you can instead use one of the provided combinators,
+//! Frequently, you won't care about the literal and you can instead use one of the provided combinators,
 //! like [`preceded`]:
 //! ```rust
 //! # use winnow::prelude::*;
@@ -111,7 +111,8 @@
 //! Sometimes, we might want to choose between two parsers; and we're happy with
 //! either being used.
 //!
-//! [`Stream::checkpoint`] helps us to retry parsing:
+//! To retry a parse result, we can save off a [`Stream::checkpoint`] and later restore the parser
+//! back to that position with [`Stream::reset`]:
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
@@ -119,29 +120,28 @@
 //!
 //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
 //!     let start = input.checkpoint();
-//!
 //!     if let Ok(output) = ("0b", parse_bin_digits).parse_next(input) {
 //!         return Ok(output);
 //!     }
 //!
-//!     input.reset(start);
+//!     input.reset(&start);
 //!     if let Ok(output) = ("0o", parse_oct_digits).parse_next(input) {
 //!         return Ok(output);
 //!     }
 //!
-//!     input.reset(start);
+//!     input.reset(&start);
 //!     if let Ok(output) = ("0d", parse_dec_digits).parse_next(input) {
 //!         return Ok(output);
 //!     }
 //!
-//!     input.reset(start);
+//!     input.reset(&start);
 //!     ("0x", parse_hex_digits).parse_next(input)
 //! }
 //!
 //! // ...
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -178,11 +178,15 @@
 //! }
 //! ```
 //!
-//! > **Warning:** the above example is for illustrative purposes and relying on `Result::Ok` or
-//! > `Result::Err` can lead to incorrect behavior. This will be clarified in later when covering
-//! > [error handling][`chapter_6`#errmode]
+//! <div class="warning">
 //!
-//! [`opt`] is a basic building block for correctly handling retrying parsing:
+//! **Warning:** the above example is for illustrative purposes and relying on `Result::Ok` or
+//! `Result::Err` can lead to incorrect behavior. This will be clarified in later when covering
+//! [error handling][`chapter_7`#error-cuts]
+//!
+//! </div>
+//!
+//! [`opt`] is a parser that encapsulates this pattern of "retry on failure":
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
@@ -202,7 +206,7 @@
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -239,7 +243,7 @@
 //! # }
 //! ```
 //!
-//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the `else`:
+//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the "else":
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
@@ -256,7 +260,7 @@
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -293,7 +297,11 @@
 //! # }
 //! ```
 //!
-//! > **Note:** [`empty`] and [`fail`] are parsers that might be useful in the `else` case.
+//! <div class="warning">
+//!
+//! **Note:** [`empty`] and [`fail`] are parsers that might be useful in the "else" case.
+//!
+//! </div>
 //!
 //! Sometimes a giant if/else-if ladder can be slow and you'd rather have a `match` statement for
 //! branches of your parser that have unique prefixes. In this case, you can use the
@@ -315,11 +323,10 @@
 //!         _ => fail,
 //!     ).parse_next(input)
 //! }
-//!
-//! // ...
+//! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -342,25 +349,29 @@
 //! #         ('a'..='f'),
 //! #     )).parse_next(input)
 //! # }
-//!
-//! fn main() {
-//!     let mut input = "0x1a2b Hello";
-//!
-//!     let digits = parse_digits.parse_next(&mut input).unwrap();
-//!
-//!     assert_eq!(input, " Hello");
-//!     assert_eq!(digits, "1a2b");
-//!
-//!     assert!(parse_digits(&mut "ghiWorld").is_err());
-//! }
+//! #
+//! # fn main() {
+//! #     let mut input = "0x1a2b Hello";
+//! #
+//! #     let digits = parse_digits.parse_next(&mut input).unwrap();
+//! #
+//! #     assert_eq!(input, " Hello");
+//! #     assert_eq!(digits, "1a2b");
+//! #
+//! #     assert!(parse_digits(&mut "ghiWorld").is_err());
+//! # }
 //! ```
 //!
-//! > **Note:** [`peek`] may be useful when [`dispatch`]ing from hints from each case's parser.
+//! <div class="warning">
+//!
+//! **Note:** [`peek`] may be useful when [`dispatch`]ing from hints from each case's parser.
+//!
+//! </div>
 //!
 //! See [`combinator`] for more alternative parsers.
 
 #![allow(unused_imports)]
-use super::chapter_6;
+use super::chapter_7;
 use crate::combinator;
 use crate::combinator::alt;
 use crate::combinator::dispatch;
diff --git a/crates/winnow/src/_tutorial/chapter_4.rs b/crates/winnow/src/_tutorial/chapter_4.rs
index 328a648..1f4d352 100644
--- a/crates/winnow/src/_tutorial/chapter_4.rs
+++ b/crates/winnow/src/_tutorial/chapter_4.rs
@@ -59,7 +59,7 @@
 //! // ...
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
diff --git a/crates/winnow/src/_tutorial/chapter_5.rs b/crates/winnow/src/_tutorial/chapter_5.rs
index 8aa719b..f095e82 100644
--- a/crates/winnow/src/_tutorial/chapter_5.rs
+++ b/crates/winnow/src/_tutorial/chapter_5.rs
@@ -35,7 +35,7 @@
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -100,7 +100,7 @@
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -136,8 +136,8 @@
 //! # }
 //! ```
 //!
-//! You'll notice that the above allows trailing `,` when we intended to not support that. We can
-//! easily fix this by using [`separated`]:
+//! You'll notice that the above allows trailing `,`. However, if that's not desired, it
+//! can easily be fixed by using [`separated`] instead of [`repeat`]:
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
@@ -163,7 +163,7 @@
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -199,9 +199,22 @@
 //! }
 //! ```
 //!
-//! If you look closely at [`repeat`], it isn't collecting directly into a [`Vec`] but
-//! [`Accumulate`] to gather the results. This lets us make more complex parsers than we did in
-//! [`chapter_2`] by accumulating the results into a `()` and [`recognize`][Parser::recognize]-ing the captured input:
+//! If you look closely at [`separated`] and [`repeat`], they aren't limited to collecting
+//! the result into a [`Vec`], but rather anything that implements the [`Accumulate`] trait.
+//! [`Accumulate`] is for instance also implemented for [`HashSet`], [`String`] and `()`.
+//!
+//! This lets us build more complex parsers than we did in
+//! [`chapter_2`] by accumulating the results into a `()` and [`take`][Parser::take]-ing
+//! the consumed input.
+//!
+//! `take` works by
+//! 1. Creating a [`checkpoint`][Stream::checkpoint]
+//! 2. Running the inner parser, in our case the `parse_list` parser, which will advance the input
+//! 3. Returning the slice from the first checkpoint to the current position.
+//!
+//! Since the result of `parse_list` gets thrown away, we
+//! accumulates into a `()` to not waste work creating an unused `Vec`.
+//!
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
@@ -210,8 +223,8 @@
 //! # use winnow::combinator::fail;
 //! # use winnow::combinator::separated;
 //! #
-//! fn recognize_list<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//!     parse_list.recognize().parse_next(input)
+//! fn take_list<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//!     parse_list.take().parse_next(input)
 //! }
 //!
 //! fn parse_list(input: &mut &str) -> PResult<()> {
@@ -230,7 +243,7 @@
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -257,7 +270,7 @@
 //! fn main() {
 //!     let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
 //!
-//!     let digits = recognize_list.parse_next(&mut input).unwrap();
+//!     let digits = take_list.parse_next(&mut input).unwrap();
 //!
 //!     assert_eq!(input, " Hello");
 //!     assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f");
@@ -274,7 +287,9 @@
 use crate::combinator::repeat;
 use crate::combinator::separated;
 use crate::stream::Accumulate;
+use crate::stream::Stream;
 use crate::Parser;
+use std::collections::HashSet;
 use std::vec::Vec;
 
 pub use super::chapter_4 as previous;
diff --git a/crates/winnow/src/_tutorial/chapter_6.rs b/crates/winnow/src/_tutorial/chapter_6.rs
index 0d54e15..c465757 100644
--- a/crates/winnow/src/_tutorial/chapter_6.rs
+++ b/crates/winnow/src/_tutorial/chapter_6.rs
@@ -1,108 +1,77 @@
-//! # Chapter 6: Error Reporting
+//! # Chapter 6: Integrating the Parser
 //!
-//! ## `Error`
+//! So far, we've highlighted how to incrementally parse, but how do we bring this all together
+//! into our application?
 //!
-//! Back in [`chapter_1`], we glossed over the `Err` side of [`PResult`].  `PResult<O>` is
-//! actually short for `PResult<O, E=ContextError>` where [`ContextError`] is a relatively cheap
-//! way of building up reasonable errors for humans.
-//!
-//! You can use [`Parser::context`] to annotate the error with custom types
-//! while unwinding to further improve the error quality.
-//!
+//! Parsers we've been working with look like:
 //! ```rust
-//! # use winnow::prelude::*;
-//! # use winnow::token::take_while;
-//! # use winnow::combinator::alt;
-//! use winnow::error::StrContext;
-//!
-//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
-//!     alt((
-//!         ("0b", parse_bin_digits).context(StrContext::Label("binary")),
-//!         ("0o", parse_oct_digits).context(StrContext::Label("octal")),
-//!         ("0d", parse_dec_digits).context(StrContext::Label("decimal")),
-//!         ("0x", parse_hex_digits).context(StrContext::Label("hexadecimal")),
-//!     )).parse_next(input)
-//! }
-//!
-//! // ...
-//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//! #     take_while(1.., (
-//! #         ('0'..='7'),
-//! #     )).parse_next(input)
-//! # }
-//! #
-//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//! #     take_while(1.., (
-//! #         ('0'..='7'),
-//! #     )).parse_next(input)
-//! # }
-//! #
-//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//! #     take_while(1.., (
-//! #         ('0'..='9'),
-//! #     )).parse_next(input)
-//! # }
-//! #
-//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//! #     take_while(1.., (
-//! #         ('0'..='9'),
-//! #         ('A'..='F'),
-//! #         ('a'..='f'),
-//! #     )).parse_next(input)
-//! # }
-//!
-//! fn main() {
-//!     let mut input = "0x1a2b Hello";
-//!
-//!     let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
-//!
-//!     assert_eq!(input, " Hello");
-//!     assert_eq!(prefix, "0x");
-//!     assert_eq!(digits, "1a2b");
-//! }
-//! ```
-//!
-//! At first glance, this looks correct but what `context` will be reported when parsing `"0b5"`?
-//! If you remember back to [`chapter_3`], [`alt`] will only report the last error by default which
-//! means when parsing `"0b5"`, the `context` will be `"hexadecimal"`.
-//!
-//! ## `ErrMode`
-//!
-//! Let's break down `PResult<O, E>` one step further:
-//! ```rust
-//! # use winnow::error::ErrorKind;
+//! # use winnow::error::ContextError;
 //! # use winnow::error::ErrMode;
-//! pub type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;
+//! # use winnow::Parser;
+//! use winnow::PResult;
+//!
+//! pub fn parser<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//!     // ...
+//! #     Ok("")
+//! }
 //! ```
-//! [`PResult`] is just a fancy wrapper around `Result` that wraps our error in an [`ErrMode`]
-//! type.
+//! 1. We have to decide what to do about the "remainder" of the `input`.
+//! 2. The [`PResult`] is not compatible with the rest of the Rust ecosystem.
+//!     Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static`
+//!     meaning:
+//!     - They implement the [`std::error::Error`] trait
+//!     - They can be sent across threads
+//!     - They are safe to be referenced across threads
+//!     - They do not borrow
 //!
-//! [`ErrMode`] is an enum with [`Backtrack`] and [`Cut`] variants (ignore [`Incomplete`] as its only
-//! relevant for [streaming][_topic::stream]). By default, errors are [`Backtrack`], meaning that
-//! other parsing branches will be attempted on failure, like the next case of an [`alt`].  [`Cut`]
-//! shortcircuits all other branches, immediately reporting the error.
+//! winnow provides [`Parser::parse`] to help with this:
+//! - Ensures we hit [`eof`]
+//! - Converts from [`PResult`] to [`Result`]
+//! - Wraps the error in [`ParseError`]
+//!   - For simple cases, [`ParseError`] provides a [`std::fmt::Display`] impl to render the error.
+//!   - For more involved cases, [`ParseError`] provides the original [`input`][ParseError::input] and the
+//!     [`offset`][ParseError::offset] of where it failed so you can capture this information in
+//!     your error, [rendering it as you wish][chapter_7#error-adaptation-and-rendering].
 //!
-//! So we can get the correct `context` by modifying the above example with [`cut_err`]:
+//! However, [`ParseError`] will still need some level of adaptation to integrate with your
+//! application's error type (like with `?`).
+//!
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
-//! # use winnow::combinator::alt;
-//! # use winnow::error::StrContext;
-//! use winnow::combinator::cut_err;
+//! # use winnow::combinator::dispatch;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! use winnow::Parser;
 //!
-//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
-//!     alt((
-//!         ("0b", cut_err(parse_bin_digits)).context(StrContext::Label("binary")),
-//!         ("0o", cut_err(parse_oct_digits)).context(StrContext::Label("octal")),
-//!         ("0d", cut_err(parse_dec_digits)).context(StrContext::Label("decimal")),
-//!         ("0x", cut_err(parse_hex_digits)).context(StrContext::Label("hexadecimal")),
-//!     )).parse_next(input)
+//! #[derive(Debug, PartialEq, Eq)]
+//! pub struct Hex(usize);
+//!
+//! impl std::str::FromStr for Hex {
+//!     type Err = anyhow::Error;
+//!
+//!     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//!         parse_digits
+//!             .map(Hex)
+//!             .parse(input)
+//!             .map_err(|e| anyhow::format_err!("{e}"))
+//!     }
 //! }
 //!
 //! // ...
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<usize> {
+//! #     dispatch!(take(2usize);
+//! #         "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! #         "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! #         "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! #         "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! #         _ => fail,
+//! #     ).parse_next(input)
+//! # }
+//! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -127,29 +96,25 @@
 //! # }
 //!
 //! fn main() {
-//!     let mut input = "0x1a2b Hello";
+//!     let input = "0x1a2b";
+//!     assert_eq!(input.parse::<Hex>().unwrap(), Hex(0x1a2b));
 //!
-//!     let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
-//!
-//!     assert_eq!(input, " Hello");
-//!     assert_eq!(prefix, "0x");
-//!     assert_eq!(digits, "1a2b");
+//!     let input = "0x1a2b Hello";
+//!     assert!(input.parse::<Hex>().is_err());
+//!     let input = "ghiHello";
+//!     assert!(input.parse::<Hex>().is_err());
 //! }
 //! ```
-//! Now, when parsing `"0b5"`, the `context` will be `"binary"`.
 
 #![allow(unused_imports)]
 use super::chapter_1;
-use super::chapter_3;
-use crate::combinator::alt;
-use crate::combinator::cut_err;
-use crate::error::ContextError;
+use super::chapter_7;
+use crate::combinator::eof;
 use crate::error::ErrMode;
-use crate::error::ErrMode::*;
-use crate::error::ErrorKind;
+use crate::error::InputError;
+use crate::error::ParseError;
 use crate::PResult;
 use crate::Parser;
-use crate::_topic;
 
 pub use super::chapter_5 as previous;
 pub use super::chapter_7 as next;
diff --git a/crates/winnow/src/_tutorial/chapter_7.rs b/crates/winnow/src/_tutorial/chapter_7.rs
index 659be3a..58ccc4d 100644
--- a/crates/winnow/src/_tutorial/chapter_7.rs
+++ b/crates/winnow/src/_tutorial/chapter_7.rs
@@ -1,76 +1,52 @@
-//! # Chapter 7: Integrating the Parser
+//! # Chapter 7: Error Reporting
 //!
-//! So far, we've highlighted how to incrementally parse, but how do we bring this all together
-//! into our application?
+//! ## Context
 //!
-//! Parsers we've been working with look like:
-//! ```rust
-//! # use winnow::error::ContextError;
-//! # use winnow::error::ErrMode;
-//! # use winnow::Parser;
-//! #
-//! pub fn parser<'s>(input: &mut &'s str) -> PResult<&'s str> {
-//!     // ...
-//! #     Ok("")
-//! }
-//!
-//! type PResult<O> = Result<
-//!     O,
-//!     ErrMode<ContextError>
-//! >;
-//! ```
-//! 1. We have to decide what to do about the "remainder" of the `input`.
-//! 2. The [`ErrMode<ContextError>`] is not compatible with the rest of the Rust ecosystem.
-//!     Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static`
-//!     meaning:
-//!     - They implement the [`std::error::Error`] trait
-//!     - They can be sent across threads
-//!     - They are safe to be referenced across threads
-//!     - They do not borrow
-//!
-//! winnow provides [`Parser::parse`] to help with this:
-//! - Ensures we hit [`eof`]
-//! - Removes the [`ErrMode`] wrapper
-//! - Wraps the error in [`ParseError`]
-//!   - Provides access to the original [`input`][ParseError::input] with the
-//!     [`offset`][ParseError::offset] of where it failed
-//!   - Provides a default renderer (via [`std::fmt::Display`])
+//! With [`Parser::parse`] we get errors that point to the failure but don't explain the reason for
+//! the failure:
 //! ```rust
 //! # use winnow::prelude::*;
 //! # use winnow::token::take_while;
-//! # use winnow::combinator::dispatch;
+//! # use winnow::combinator::alt;
 //! # use winnow::token::take;
 //! # use winnow::combinator::fail;
-//! use winnow::Parser;
-//!
-//! #[derive(Debug, PartialEq, Eq)]
-//! pub struct Hex(usize);
-//!
-//! impl std::str::FromStr for Hex {
-//!     type Err = String;
-//!
-//!     fn from_str(input: &str) -> Result<Self, Self::Err> {
-//!         parse_digits
-//!             .map(Hex)
-//!             .parse(input)
-//!             .map_err(|e| e.to_string())
-//!     }
-//! }
-//!
+//! # use winnow::Parser;
+//! #
+//! # #[derive(Debug, PartialEq, Eq)]
+//! # pub struct Hex(usize);
+//! #
+//! # impl std::str::FromStr for Hex {
+//! #     type Err = String;
+//! #
+//! #     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//! #             .parse(input)
+//! #             .map_err(|e| e.to_string())
+//! #     }
+//! # }
+//! #
 //! // ...
-//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<usize> {
-//! #     dispatch!(take(2usize);
-//! #         "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
-//! #         "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
-//! #         "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
-//! #         "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
-//! #         _ => fail,
-//! #     ).parse_next(input)
+//!
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! #     alt((
+//! #         ("0b", parse_bin_digits),
+//! #         ("0o", parse_oct_digits),
+//! #         ("0d", parse_dec_digits),
+//! #         ("0x", parse_hex_digits),
+//! #     )).parse_next(input)
 //! # }
 //! #
 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
 //! #     take_while(1.., (
-//! #         ('0'..='7'),
+//! #         ('0'..='1'),
 //! #     )).parse_next(input)
 //! # }
 //! #
@@ -93,26 +69,659 @@
 //! #         ('a'..='f'),
 //! #     )).parse_next(input)
 //! # }
-//!
 //! fn main() {
-//!     let input = "0x1a2b";
-//!     assert_eq!(input.parse::<Hex>().unwrap(), Hex(0x1a2b));
+//!     let input = "0xZZ";
+//!     let error = "\
+//! 0xZZ
+//!   ^
+//! ";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err(), error);
+//! }
+//! ```
 //!
-//!     let input = "0x1a2b Hello";
-//!     assert!(input.parse::<Hex>().is_err());
-//!     let input = "ghiHello";
-//!     assert!(input.parse::<Hex>().is_err());
+//! Back in [`chapter_1`], we glossed over the `Err` variant of [`PResult`].  `PResult<O>` is
+//! actually short for `PResult<O, E=ContextError>` where [`ContextError`] is a relatively cheap
+//! way of building up reasonable errors for humans.
+//!
+//! You can use [`Parser::context`] to annotate the error with custom types
+//! while unwinding to further clarify the error:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! # use winnow::Parser;
+//! use winnow::error::StrContext;
+//! use winnow::error::StrContextValue;
+//!
+//! #
+//! # #[derive(Debug, PartialEq, Eq)]
+//! # pub struct Hex(usize);
+//! #
+//! # impl std::str::FromStr for Hex {
+//! #     type Err = String;
+//! #
+//! #     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//! #             .parse(input)
+//! #             .map_err(|e| e.to_string())
+//! #     }
+//! # }
+//! #
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//!     alt((
+//!         ("0b", parse_bin_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("binary"))),
+//!         ("0o", parse_oct_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("octal"))),
+//!         ("0d", parse_dec_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("decimal"))),
+//!         ("0x", parse_hex_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
+//!     )).parse_next(input)
+//! }
+//!
+//! // ...
+//!
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='1'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='7'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #         ('A'..='F'),
+//! #         ('a'..='f'),
+//! #     )).parse_next(input)
+//! # }
+//! fn main() {
+//!     let input = "0xZZ";
+//!     let error = "\
+//! 0xZZ
+//!   ^
+//! invalid digit
+//! expected hexadecimal";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err(), error);
+//! }
+//! ```
+//!
+//! If you remember back to [`chapter_3`], [`alt`] will only report the last error.
+//! So if the parsers fail for any reason, like a bad radix, it will be reported as an invalid
+//! hexadecimal value:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! # use winnow::Parser;
+//! # use winnow::error::StrContext;
+//! # use winnow::error::StrContextValue;
+//! #
+//! #
+//! # #[derive(Debug, PartialEq, Eq)]
+//! # pub struct Hex(usize);
+//! #
+//! # impl std::str::FromStr for Hex {
+//! #     type Err = String;
+//! #
+//! #     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//! #             .parse(input)
+//! #             .map_err(|e| e.to_string())
+//! #     }
+//! # }
+//! #
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! #     alt((
+//! #         ("0b", parse_bin_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("binary"))),
+//! #         ("0o", parse_oct_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("octal"))),
+//! #         ("0d", parse_dec_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("decimal"))),
+//! #         ("0x", parse_hex_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='1'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='7'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #         ('A'..='F'),
+//! #         ('a'..='f'),
+//! #     )).parse_next(input)
+//! # }
+//! fn main() {
+//!     let input = "100";
+//!     let error = "\
+//! 100
+//! ^
+//! invalid digit
+//! expected hexadecimal";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err(), error);
+//! }
+//! ```
+//! We can improve this with [`fail`]:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! # use winnow::Parser;
+//! use winnow::error::StrContext;
+//! use winnow::error::StrContextValue;
+//!
+//! #
+//! # #[derive(Debug, PartialEq, Eq)]
+//! # pub struct Hex(usize);
+//! #
+//! # impl std::str::FromStr for Hex {
+//! #     type Err = String;
+//! #
+//! #     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//! #             .parse(input)
+//! #             .map_err(|e| e.to_string())
+//! #     }
+//! # }
+//! #
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//!     alt((
+//!         ("0b", parse_bin_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("binary"))),
+//!         ("0o", parse_oct_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("octal"))),
+//!         ("0d", parse_dec_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("decimal"))),
+//!         ("0x", parse_hex_digits)
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
+//!         fail
+//!           .context(StrContext::Label("radix prefix"))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
+//!     )).parse_next(input)
+//! }
+//!
+//! // ...
+//!
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='1'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='7'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #         ('A'..='F'),
+//! #         ('a'..='f'),
+//! #     )).parse_next(input)
+//! # }
+//! fn main() {
+//!     let input = "100";
+//!     let error = "\
+//! 100
+//! ^
+//! invalid radix prefix
+//! expected `0b`, `0o`, `0d`, `0x`";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err(), error);
+//! }
+//! ```
+//!
+//! ## Error Cuts
+//!
+//! We still have the issue that we are falling-through when the radix is valid but the digits
+//! don't match it:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! # use winnow::Parser;
+//! # use winnow::error::StrContext;
+//! # use winnow::error::StrContextValue;
+//! #
+//! #
+//! # #[derive(Debug, PartialEq, Eq)]
+//! # pub struct Hex(usize);
+//! #
+//! # impl std::str::FromStr for Hex {
+//! #     type Err = String;
+//! #
+//! #     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//! #             .parse(input)
+//! #             .map_err(|e| e.to_string())
+//! #     }
+//! # }
+//! #
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! #     alt((
+//! #         ("0b", parse_bin_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("binary"))),
+//! #         ("0o", parse_oct_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("octal"))),
+//! #         ("0d", parse_dec_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("decimal"))),
+//! #         ("0x", parse_hex_digits)
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
+//! #         fail
+//! #           .context(StrContext::Label("radix prefix"))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='1'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='7'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #         ('A'..='F'),
+//! #         ('a'..='f'),
+//! #     )).parse_next(input)
+//! # }
+//! fn main() {
+//!     let input = "0b5";
+//!     let error = "\
+//! 0b5
+//! ^
+//! invalid radix prefix
+//! expected `0b`, `0o`, `0d`, `0x`";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err(), error);
+//! }
+//! ```
+//!
+//! Let's break down `PResult<O, E>` one step further:
+//! ```rust
+//! # use winnow::error::ErrorKind;
+//! # use winnow::error::ErrMode;
+//! pub type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;
+//! ```
+//! [`PResult`] is just a fancy wrapper around `Result` that wraps our error in an [`ErrMode`]
+//! type.
+//!
+//! [`ErrMode`] is an enum with [`Backtrack`] and [`Cut`] variants (ignore [`Incomplete`] as its only
+//! relevant for [streaming][_topic::stream]). By default, errors are [`Backtrack`], meaning that
+//! other parsing branches will be attempted on failure, like the next case of an [`alt`].  [`Cut`]
+//! shortcircuits all other branches, immediately reporting the error.
+//!
+//! So we can get the correct `context` by modifying the above example with [`cut_err`]:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! # use winnow::Parser;
+//! # use winnow::error::StrContext;
+//! # use winnow::error::StrContextValue;
+//! use winnow::combinator::cut_err;
+//!
+//! #
+//! # #[derive(Debug, PartialEq, Eq)]
+//! # pub struct Hex(usize);
+//! #
+//! # impl std::str::FromStr for Hex {
+//! #     type Err = String;
+//! #
+//! #     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//! #             .parse(input)
+//! #             .map_err(|e| e.to_string())
+//! #     }
+//! # }
+//! #
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//!     alt((
+//!         ("0b", cut_err(parse_bin_digits))
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("binary"))),
+//!         ("0o", cut_err(parse_oct_digits))
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("octal"))),
+//!         ("0d", cut_err(parse_dec_digits))
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("decimal"))),
+//!         ("0x", cut_err(parse_hex_digits))
+//!           .context(StrContext::Label("digit"))
+//!           .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
+//!         fail
+//!           .context(StrContext::Label("radix prefix"))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
+//!           .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
+//!     )).parse_next(input)
+//! }
+//!
+//! // ...
+//!
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='1'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='7'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #         ('A'..='F'),
+//! #         ('a'..='f'),
+//! #     )).parse_next(input)
+//! # }
+//! fn main() {
+//!     let input = "0b5";
+//!     let error = "\
+//! 0b5
+//!   ^
+//! invalid digit
+//! expected binary";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err(), error);
+//! }
+//! ```
+//!
+//! ## Error Adaptation and Rendering
+//!
+//! While Winnow can provide basic rendering of errors, your application can have various demands
+//! beyond the basics provided like
+//! - Correctly reporting columns with unicode
+//! - Conforming to a specific layout
+//!
+//! For example, to get rustc-like errors with [`annotate-snippets`](https://crates.io/crates/annotate-snippets):
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! # use winnow::Parser;
+//! # use winnow::error::ParseError;
+//! # use winnow::error::ContextError;
+//! # use winnow::error::StrContext;
+//! # use winnow::error::StrContextValue;
+//! # use winnow::combinator::cut_err;
+//! #
+//! #
+//! #[derive(Debug, PartialEq, Eq)]
+//! pub struct Hex(usize);
+//!
+//! impl std::str::FromStr for Hex {
+//!     type Err = HexError;
+//!
+//!     fn from_str(input: &str) -> Result<Self, Self::Err> {
+//!         // ...
+//! #         parse_digits
+//! #             .try_map(|(t, v)| match t {
+//! #                "0b" => usize::from_str_radix(v, 2),
+//! #                "0o" => usize::from_str_radix(v, 8),
+//! #                "0d" => usize::from_str_radix(v, 10),
+//! #                "0x" => usize::from_str_radix(v, 16),
+//! #                _ => unreachable!("`parse_digits` doesn't return `{t}`"),
+//! #              })
+//! #             .map(Hex)
+//!             .parse(input)
+//!             .map_err(|e| HexError::from_parse(e, input))
+//!     }
+//! }
+//!
+//! #[derive(Debug)]
+//! pub struct HexError {
+//!     message: String,
+//!     // Byte spans are tracked, rather than line and column.
+//!     // This makes it easier to operate on programmatically
+//!     // and doesn't limit us to one definition for column count
+//!     // which can depend on the output medium and application.
+//!     span: std::ops::Range<usize>,
+//!     input: String,
+//! }
+//!
+//! impl HexError {
+//!     fn from_parse(error: ParseError<&str, ContextError>, input: &str) -> Self {
+//!         // The default renderer for `ContextError` is still used but that can be
+//!         // customized as well to better fit your needs.
+//!         let message = error.inner().to_string();
+//!         let input = input.to_owned();
+//!         let start = error.offset();
+//!         // Assume the error span is only for the first `char`.
+//!         // Semantic errors are free to choose the entire span returned by `Parser::with_span`.
+//!         let end = (start + 1..).find(|e| input.is_char_boundary(*e)).unwrap_or(start);
+//!         Self {
+//!             message,
+//!             span: start..end,
+//!             input,
+//!         }
+//!     }
+//! }
+//!
+//! impl std::fmt::Display for HexError {
+//!     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+//!         let message = annotate_snippets::Level::Error.title(&self.message)
+//!             .snippet(annotate_snippets::Snippet::source(&self.input)
+//!                 .fold(true)
+//!                 .annotation(annotate_snippets::Level::Error.span(self.span.clone()))
+//!             );
+//!         let renderer = annotate_snippets::Renderer::plain();
+//!         let rendered = renderer.render(message);
+//!         rendered.fmt(f)
+//!     }
+//! }
+//!
+//! impl std::error::Error for HexError {}
+//!
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! #     alt((
+//! #         ("0b", cut_err(parse_bin_digits))
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("binary"))),
+//! #         ("0o", cut_err(parse_oct_digits))
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("octal"))),
+//! #         ("0d", cut_err(parse_dec_digits))
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("decimal"))),
+//! #         ("0x", cut_err(parse_hex_digits))
+//! #           .context(StrContext::Label("digit"))
+//! #           .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))),
+//! #         fail
+//! #           .context(StrContext::Label("radix prefix"))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0b")))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0o")))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0d")))
+//! #           .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='1'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='7'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #     )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! #     take_while(1.., (
+//! #         ('0'..='9'),
+//! #         ('A'..='F'),
+//! #         ('a'..='f'),
+//! #     )).parse_next(input)
+//! # }
+//! fn main() {
+//!     let input = "0b5";
+//!     let error = "\
+//! error: invalid digit
+//! expected binary
+//!   |
+//! 1 | 0b5
+//!   |   ^
+//!   |";
+//!     assert_eq!(input.parse::<Hex>().unwrap_err().to_string(), error);
 //! }
 //! ```
 
 #![allow(unused_imports)]
 use super::chapter_1;
-use crate::combinator::eof;
+use super::chapter_3;
+use crate::combinator::alt;
+use crate::combinator::cut_err;
+use crate::combinator::fail;
+use crate::error::ContextError;
 use crate::error::ErrMode;
-use crate::error::InputError;
-use crate::error::ParseError;
+use crate::error::ErrMode::*;
+use crate::error::ErrorKind;
 use crate::PResult;
 use crate::Parser;
+use crate::_topic;
 
 pub use super::chapter_6 as previous;
 pub use super::chapter_8 as next;
diff --git a/crates/winnow/src/_tutorial/chapter_8.rs b/crates/winnow/src/_tutorial/chapter_8.rs
index 6ff8f29..d594fc4 100644
--- a/crates/winnow/src/_tutorial/chapter_8.rs
+++ b/crates/winnow/src/_tutorial/chapter_8.rs
@@ -1,7 +1,8 @@
 //! # Chapter 8: Debugging
 //!
 //! When things inevitably go wrong, you can introspect the parsing state by running your test case
-//! with `--features debug`:
+//! with `--features winnow/debug`:
+//!
 //! ![Trace output from string example](https://raw.githubusercontent.com/winnow-rs/winnow/main/assets/trace.svg "Example output")
 //!
 //! You can extend your own parsers to show up by wrapping their body with
diff --git a/crates/winnow/src/ascii/mod.rs b/crates/winnow/src/ascii/mod.rs
index 8e6a480..bdcfc5c 100644
--- a/crates/winnow/src/ascii/mod.rs
+++ b/crates/winnow/src/ascii/mod.rs
@@ -9,14 +9,19 @@
 
 use crate::combinator::alt;
 use crate::combinator::cut_err;
+use crate::combinator::dispatch;
+use crate::combinator::empty;
+use crate::combinator::fail;
 use crate::combinator::opt;
 use crate::combinator::trace;
 use crate::error::ParserError;
 use crate::error::{ErrMode, ErrorKind, Needed};
+use crate::stream::FindSlice;
 use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial};
 use crate::stream::{Compare, CompareResult};
+use crate::token::any;
 use crate::token::one_of;
-use crate::token::take_till;
+use crate::token::take_until;
 use crate::token::take_while;
 use crate::PResult;
 use crate::Parser;
@@ -54,7 +59,18 @@
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn crlf<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::crlf.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -81,11 +97,10 @@
 /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn crlf<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn crlf<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    I: Compare<&'static str>,
+    Input: StreamIsPartial + Stream + Compare<&'static str>,
+    Error: ParserError<Input>,
 {
     trace("crlf", "\r\n").parse_next(input)
 }
@@ -94,7 +109,18 @@
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn till_line_ending<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::till_line_ending.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -120,21 +146,20 @@
 /// # use winnow::Partial;
 /// # use winnow::ascii::till_line_ending;
 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Ok((Partial::new("\r\nc"), "ab")));
-/// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::new(1))));
-/// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::Unknown)));
+/// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::Unknown)));
 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rb\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rb\nc"), ErrorKind::Tag ))));
 /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rbc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rbc"), ErrorKind::Tag ))));
 /// ```
 #[inline(always)]
-pub fn till_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn till_line_ending<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    I: Compare<&'static str>,
-    <I as Stream>::Token: AsChar + Clone,
+    Input: StreamIsPartial + Stream + Compare<&'static str> + FindSlice<(char, char)>,
+    <Input as Stream>::Token: AsChar + Clone,
+    Error: ParserError<Input>,
 {
-    trace("till_line_ending", move |input: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() {
+    trace("till_line_ending", move |input: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() {
             till_line_ending_::<_, _, true>(input)
         } else {
             till_line_ending_::<_, _, false>(input)
@@ -143,19 +168,6 @@
     .parse_next(input)
 }
 
-/// Deprecated, replaced with [`till_line_ending`]
-#[deprecated(since = "0.5.35", note = "Replaced with `till_line_ending`")]
-#[inline(always)]
-pub fn not_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    I: Compare<&'static str>,
-    <I as Stream>::Token: AsChar + Clone,
-{
-    till_line_ending(input)
-}
-
 fn till_line_ending_<I, E: ParserError<I>, const PARTIAL: bool>(
     input: &mut I,
 ) -> PResult<<I as Stream>::Slice, E>
@@ -163,14 +175,20 @@
     I: StreamIsPartial,
     I: Stream,
     I: Compare<&'static str>,
+    I: FindSlice<(char, char)>,
     <I as Stream>::Token: AsChar + Clone,
 {
-    let res = take_till(0.., ('\r', '\n')).parse_next(input)?;
-    if input.compare("\r") == CompareResult::Ok {
+    let res = match take_until(0.., ('\r', '\n')).parse_next(input) {
+        Ok(slice) => slice,
+        Err(ErrMode::Backtrack(_)) => input.finish(),
+        Err(err) => {
+            return Err(err);
+        }
+    };
+    if matches!(input.compare("\r"), CompareResult::Ok(_)) {
         let comp = input.compare("\r\n");
         match comp {
-            //FIXME: calculate the right index
-            CompareResult::Ok => {}
+            CompareResult::Ok(_) => {}
             CompareResult::Incomplete if PARTIAL && input.is_partial() => {
                 return Err(ErrMode::Incomplete(Needed::Unknown));
             }
@@ -187,7 +205,18 @@
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn line_ending<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::line_ending.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -214,11 +243,10 @@
 /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn line_ending<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    I: Compare<&'static str>,
+    Input: StreamIsPartial + Stream + Compare<&'static str>,
+    Error: ParserError<Input>,
 {
     trace("line_ending", alt(("\n", "\r\n"))).parse_next(input)
 }
@@ -227,7 +255,18 @@
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn newline(input: &mut &str) -> PResult<char>
+/// # {
+/// #     winnow::ascii::newline.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -240,8 +279,8 @@
 /// }
 ///
 /// assert_eq!(parser.parse_peek("\nc"), Ok(("c", '\n')));
-/// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify))));
-/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
 /// ```
 ///
 /// ```
@@ -250,7 +289,7 @@
 /// # use winnow::Partial;
 /// # use winnow::ascii::newline;
 /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\nc")), Ok((Partial::new("c"), '\n')));
-/// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify))));
+/// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Tag))));
 /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
@@ -258,16 +297,27 @@
 where
     I: StreamIsPartial,
     I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    I: Compare<char>,
 {
-    trace("newline", '\n'.map(AsChar::as_char)).parse_next(input)
+    trace("newline", '\n').parse_next(input)
 }
 
 /// Matches a tab character `'\t'`.
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn tab(input: &mut &str) -> PResult<char>
+/// # {
+/// #     winnow::ascii::tab.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -280,8 +330,8 @@
 /// }
 ///
 /// assert_eq!(parser.parse_peek("\tc"), Ok(("c", '\t')));
-/// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify))));
-/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
 /// ```
 ///
 /// ```
@@ -290,17 +340,16 @@
 /// # use winnow::Partial;
 /// # use winnow::ascii::tab;
 /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\tc")), Ok((Partial::new("c"), '\t')));
-/// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify))));
+/// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Tag))));
 /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn tab<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error>
+pub fn tab<Input, Error>(input: &mut Input) -> PResult<char, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    Input: StreamIsPartial + Stream + Compare<char>,
+    Error: ParserError<Input>,
 {
-    trace("tab", '\t'.map(AsChar::as_char)).parse_next(input)
+    trace("tab", '\t').parse_next(input)
 }
 
 /// Recognizes zero or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'`
@@ -308,9 +357,20 @@
 /// *Complete version*: Will return the whole input if no terminating token is found (a non
 /// alphabetic character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non alphabetic character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn alpha0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::alpha0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -336,11 +396,11 @@
 /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn alpha0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn alpha0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("alpha0", take_while(0.., AsChar::is_alpha)).parse_next(input)
 }
@@ -350,9 +410,20 @@
 /// *Complete version*: Will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found  (a non alphabetic character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non alphabetic character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn alpha1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::alpha1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -378,11 +449,11 @@
 /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn alpha1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn alpha1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("alpha1", take_while(1.., AsChar::is_alpha)).parse_next(input)
 }
@@ -392,9 +463,20 @@
 /// *Complete version*: Will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found (a non digit character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non digit character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn digit0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::digit0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -421,11 +503,11 @@
 /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn digit0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("digit0", take_while(0.., AsChar::is_dec_digit)).parse_next(input)
 }
@@ -435,9 +517,20 @@
 /// *Complete version*: Will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found (a non digit character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non digit character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn digit1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::digit1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -480,11 +573,11 @@
 /// assert!(parser.parse_peek("b").is_err());
 /// ```
 #[inline(always)]
-pub fn digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn digit1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("digit1", take_while(1.., AsChar::is_dec_digit)).parse_next(input)
 }
@@ -494,9 +587,20 @@
 ///
 /// *Complete version*: Will return the whole input if no terminating token is found (a non hexadecimal digit character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non hexadecimal digit character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn hex_digit0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::hex_digit0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -522,11 +626,11 @@
 /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn hex_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn hex_digit0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("hex_digit0", take_while(0.., AsChar::is_hex_digit)).parse_next(input)
 }
@@ -537,9 +641,20 @@
 /// *Complete version*: Will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found (a non hexadecimal digit character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non hexadecimal digit character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn hex_digit1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::hex_digit1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -565,11 +680,11 @@
 /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn hex_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn hex_digit1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("hex_digit1", take_while(1.., AsChar::is_hex_digit)).parse_next(input)
 }
@@ -579,9 +694,20 @@
 /// *Complete version*: Will return the whole input if no terminating token is found (a non octal
 /// digit character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non octal digit character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn oct_digit0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::oct_digit0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -607,11 +733,12 @@
 /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn oct_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn oct_digit0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial,
+    Input: Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("oct_digit0", take_while(0.., AsChar::is_oct_digit)).parse_next(input)
 }
@@ -621,9 +748,20 @@
 /// *Complete version*: Will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found (a non octal digit character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non octal digit character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn oct_digit1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::oct_digit1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -649,11 +787,11 @@
 /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn oct_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn oct_digit1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("oct_digit0", take_while(1.., AsChar::is_oct_digit)).parse_next(input)
 }
@@ -663,9 +801,20 @@
 /// *Complete version*: Will return the whole input if no terminating token is found (a non
 /// alphanumerical character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non alphanumerical character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn alphanumeric0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::alphanumeric0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -691,11 +840,11 @@
 /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn alphanumeric0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn alphanumeric0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("alphanumeric0", take_while(0.., AsChar::is_alphanum)).parse_next(input)
 }
@@ -705,9 +854,20 @@
 /// *Complete version*: Will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found (a non alphanumerical character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non alphanumerical character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn alphanumeric1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::alphanumeric1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -733,11 +893,11 @@
 /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn alphanumeric1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn alphanumeric1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("alphanumeric1", take_while(1.., AsChar::is_alphanum)).parse_next(input)
 }
@@ -747,9 +907,20 @@
 /// *Complete version*: Will return the whole input if no terminating token is found (a non space
 /// character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non space character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn space0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::space0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -762,23 +933,34 @@
 /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn space0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn space0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("space0", take_while(0.., AsChar::is_space)).parse_next(input)
 }
 
-/// Recognizes zero or more spaces and tabs.
+/// Recognizes one or more spaces and tabs.
 ///
 /// *Complete version*: Will return the whole input if no terminating token is found (a non space
 /// character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non space character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn space1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::space1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -804,11 +986,11 @@
 /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn space1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn space1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    Error: ParserError<Input>,
 {
     trace("space1", take_while(1.., AsChar::is_space)).parse_next(input)
 }
@@ -818,9 +1000,20 @@
 /// *Complete version*: will return the whole input if no terminating token is found (a non space
 /// character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non space character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn multispace0<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::multispace0.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -846,11 +1039,11 @@
 /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn multispace0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn multispace0<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar + Clone,
+    Error: ParserError<Input>,
 {
     trace("multispace0", take_while(0.., (' ', '\t', '\r', '\n'))).parse_next(input)
 }
@@ -860,9 +1053,20 @@
 /// *Complete version*: will return an error if there's not enough input data,
 /// or the whole input if no terminating token is found (a non space character).
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
 /// or if no terminating token is found (a non space character).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn multispace1<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::ascii::multispace1.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```
@@ -888,11 +1092,11 @@
 /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn multispace1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn multispace1<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar + Clone,
+    Error: ParserError<Input>,
 {
     trace("multispace1", take_while(1.., (' ', '\t', '\r', '\n'))).parse_next(input)
 }
@@ -901,159 +1105,84 @@
 ///
 /// *Complete version*: can parse until the end of input.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] into a `u32`:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn dec_uint(input: &mut &str) -> PResult<u32>
+/// # {
+/// #     winnow::ascii::dec_uint.parse_next(input)
+/// # }
+/// ```
 #[doc(alias = "u8")]
 #[doc(alias = "u16")]
 #[doc(alias = "u32")]
 #[doc(alias = "u64")]
 #[doc(alias = "u128")]
-pub fn dec_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+pub fn dec_uint<Input, Output, Error>(input: &mut Input) -> PResult<Output, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
-    O: Uint,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Slice: AsBStr,
+    <Input as Stream>::Token: AsChar + Clone,
+    Output: Uint,
+    Error: ParserError<Input>,
 {
-    trace("dec_uint", move |input: &mut I| {
-        if input.eof_offset() == 0 {
-            if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
-                return Err(ErrMode::Incomplete(Needed::new(1)));
-            } else {
-                return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
-            }
-        }
-
-        let mut value = O::default();
-        for (offset, c) in input.iter_offsets() {
-            match c.as_char().to_digit(10) {
-                Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
-                    let d = d as u8;
-                    v.checked_add(d, sealed::SealedMarker)
-                }) {
-                    None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)),
-                    Some(v) => value = v,
-                },
-                None => {
-                    if offset == 0 {
-                        return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
-                    } else {
-                        let _ = input.next_slice(offset);
-                        return Ok(value);
-                    }
-                }
-            }
-        }
-
-        if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
-            Err(ErrMode::Incomplete(Needed::new(1)))
-        } else {
-            let _ = input.finish();
-            Ok(value)
-        }
+    trace("dec_uint", move |input: &mut Input| {
+        alt(((one_of('1'..='9'), digit0).void(), one_of('0').void()))
+            .take()
+            .verify_map(|s: <Input as Stream>::Slice| {
+                let s = s.as_bstr();
+                // SAFETY: Only 7-bit ASCII characters are parsed
+                let s = unsafe { crate::lib::std::str::from_utf8_unchecked(s) };
+                Output::try_from_dec_uint(s)
+            })
+            .parse_next(input)
     })
     .parse_next(input)
 }
 
 /// Metadata for parsing unsigned integers, see [`dec_uint`]
-pub trait Uint: Default {
+pub trait Uint: Sized {
     #[doc(hidden)]
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
-    #[doc(hidden)]
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
+    fn try_from_dec_uint(slice: &str) -> Option<Self>;
 }
 
 impl Uint for u8 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
+    fn try_from_dec_uint(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Uint for u16 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
+    fn try_from_dec_uint(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Uint for u32 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
+    fn try_from_dec_uint(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Uint for u64 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
+    fn try_from_dec_uint(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Uint for u128 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
+    fn try_from_dec_uint(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
-/// Deprecated since v0.5.17
-impl Uint for i8 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
-    }
-}
-
-/// Deprecated since v0.5.17
-impl Uint for i16 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
-    }
-}
-
-/// Deprecated since v0.5.17
-impl Uint for i32 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
-    }
-}
-
-/// Deprecated since v0.5.17
-impl Uint for i64 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
-    }
-}
-
-/// Deprecated since v0.5.17
-impl Uint for i128 {
-    fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_mul(by as Self)
-    }
-    fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_add(by as Self)
+impl Uint for usize {
+    fn try_from_dec_uint(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
@@ -1061,104 +1190,89 @@
 ///
 /// *Complete version*: can parse until the end of input.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] into an `i32`:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn dec_int(input: &mut &str) -> PResult<i32>
+/// # {
+/// #     winnow::ascii::dec_int.parse_next(input)
+/// # }
+/// ```
 #[doc(alias = "i8")]
 #[doc(alias = "i16")]
 #[doc(alias = "i32")]
 #[doc(alias = "i64")]
 #[doc(alias = "i128")]
-pub fn dec_int<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+pub fn dec_int<Input, Output, Error>(input: &mut Input) -> PResult<Output, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
-    O: Int,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Slice: AsBStr,
+    <Input as Stream>::Token: AsChar + Clone,
+    Output: Int,
+    Error: ParserError<Input>,
 {
-    trace("dec_int", move |input: &mut I| {
-        fn sign(token: impl AsChar) -> bool {
-            let token = token.as_char();
-            token == '+' || token == '-'
-        }
-        let sign = opt(crate::token::one_of(sign).map(AsChar::as_char))
-            .map(|c| c != Some('-'))
-            .parse_next(input)?;
-
-        if input.eof_offset() == 0 {
-            if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
-                return Err(ErrMode::Incomplete(Needed::new(1)));
-            } else {
-                return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
-            }
-        }
-
-        let mut value = O::default();
-        for (offset, c) in input.iter_offsets() {
-            match c.as_char().to_digit(10) {
-                Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
-                    let d = d as u8;
-                    if sign {
-                        v.checked_add(d, sealed::SealedMarker)
-                    } else {
-                        v.checked_sub(d, sealed::SealedMarker)
-                    }
-                }) {
-                    None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)),
-                    Some(v) => value = v,
-                },
-                None => {
-                    if offset == 0 {
-                        return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
-                    } else {
-                        let _ = input.next_slice(offset);
-                        return Ok(value);
-                    }
-                }
-            }
-        }
-
-        if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
-            Err(ErrMode::Incomplete(Needed::new(1)))
-        } else {
-            let _ = input.finish();
-            Ok(value)
-        }
+    trace("dec_int", move |input: &mut Input| {
+        let sign = opt(dispatch! {any.map(AsChar::as_char);
+            '+' => empty.value(true),
+            '-' => empty.value(false),
+            _ => fail,
+        });
+        alt(((sign, one_of('1'..='9'), digit0).void(), one_of('0').void()))
+            .take()
+            .verify_map(|s: <Input as Stream>::Slice| {
+                let s = s.as_bstr();
+                // SAFETY: Only 7-bit ASCII characters are parsed
+                let s = unsafe { crate::lib::std::str::from_utf8_unchecked(s) };
+                Output::try_from_dec_int(s)
+            })
+            .parse_next(input)
     })
     .parse_next(input)
 }
 
 /// Metadata for parsing signed integers, see [`dec_int`]
-pub trait Int: Uint {
+pub trait Int: Sized {
     #[doc(hidden)]
-    fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
+    fn try_from_dec_int(slice: &str) -> Option<Self>;
 }
 
 impl Int for i8 {
-    fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_sub(by as Self)
+    fn try_from_dec_int(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Int for i16 {
-    fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_sub(by as Self)
+    fn try_from_dec_int(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Int for i32 {
-    fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_sub(by as Self)
+    fn try_from_dec_int(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Int for i64 {
-    fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_sub(by as Self)
+    fn try_from_dec_int(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
 impl Int for i128 {
-    fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
-        self.checked_sub(by as Self)
+    fn try_from_dec_int(slice: &str) -> Option<Self> {
+        slice.parse().ok()
+    }
+}
+
+impl Int for isize {
+    fn try_from_dec_int(slice: &str) -> Option<Self> {
+        slice.parse().ok()
     }
 }
 
@@ -1167,9 +1281,20 @@
 /// *Complete version*: Will parse until the end of input if it has fewer characters than the type
 /// supports.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input
 /// is hit before a hard boundary (non-hex character, more characters than supported).
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] into a `u32`:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn hex_uint(input: &mut &str) -> PResult<u32>
+/// # {
+/// #     winnow::ascii::hex_uint.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```rust
@@ -1201,22 +1326,22 @@
 /// assert_eq!(parser.parse_peek(Partial::new(&b"ggg"[..])), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"ggg"[..]), ErrorKind::Slice))));
 /// ```
 #[inline]
-pub fn hex_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+pub fn hex_uint<Input, Output, Error>(input: &mut Input) -> PResult<Output, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    O: HexUint,
-    <I as Stream>::Token: AsChar,
-    <I as Stream>::Slice: AsBStr,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: AsChar,
+    <Input as Stream>::Slice: AsBStr,
+    Output: HexUint,
+    Error: ParserError<Input>,
 {
-    trace("hex_uint", move |input: &mut I| {
+    trace("hex_uint", move |input: &mut Input| {
         let invalid_offset = input
             .offset_for(|c| {
                 let c = c.as_char();
                 !"0123456789abcdefABCDEF".contains(c)
             })
             .unwrap_or_else(|| input.eof_offset());
-        let max_nibbles = O::max_nibbles(sealed::SealedMarker);
+        let max_nibbles = Output::max_nibbles(sealed::SealedMarker);
         let max_offset = input.offset_at(max_nibbles);
         let offset = match max_offset {
             Ok(max_offset) => {
@@ -1228,7 +1353,7 @@
                 }
             }
             Err(_) => {
-                if <I as StreamIsPartial>::is_partial_supported()
+                if <Input as StreamIsPartial>::is_partial_supported()
                     && input.is_partial()
                     && invalid_offset == input.eof_offset()
                 {
@@ -1245,12 +1370,12 @@
         }
         let parsed = input.next_slice(offset);
 
-        let mut res = O::default();
+        let mut res = Output::default();
         for c in parsed.as_bstr() {
             let nibble = *c as char;
             let nibble = nibble.to_digit(16).unwrap_or(0) as u8;
-            let nibble = O::from(nibble);
-            res = (res << O::from(4)) + nibble;
+            let nibble = Output::from(nibble);
+            res = (res << Output::from(4)) + nibble;
         }
 
         Ok(res)
@@ -1305,7 +1430,18 @@
 ///
 /// *Complete version*: Can parse until the end of input.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] into an `f64`:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn float(input: &mut &str) -> PResult<f64>
+/// # {
+/// #     winnow::ascii::float.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -1346,18 +1482,16 @@
 #[doc(alias = "f32")]
 #[doc(alias = "double")]
 #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
-pub fn float<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+pub fn float<Input, Output, Error>(input: &mut Input) -> PResult<Output, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    I: Compare<&'static str>,
-    <I as Stream>::Slice: ParseSlice<O>,
-    <I as Stream>::Token: AsChar + Clone,
-    <I as Stream>::IterOffsets: Clone,
-    I: AsBStr,
+    Input: StreamIsPartial + Stream + Compare<Caseless<&'static str>> + Compare<char> + AsBStr,
+    <Input as Stream>::Slice: ParseSlice<Output>,
+    <Input as Stream>::Token: AsChar + Clone,
+    <Input as Stream>::IterOffsets: Clone,
+    Error: ParserError<Input>,
 {
-    trace("float", move |input: &mut I| {
-        let s = recognize_float_or_exceptions(input)?;
+    trace("float", move |input: &mut Input| {
+        let s = take_float_or_exceptions(input)?;
         s.parse_slice()
             .ok_or_else(|| ErrMode::from_error_kind(input, ErrorKind::Verify))
     })
@@ -1365,37 +1499,39 @@
 }
 
 #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
-#[allow(deprecated)]
-fn recognize_float_or_exceptions<I, E: ParserError<I>>(
-    input: &mut I,
-) -> PResult<<I as Stream>::Slice, E>
+fn take_float_or_exceptions<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
 where
     I: StreamIsPartial,
     I: Stream,
-    I: Compare<&'static str>,
+    I: Compare<Caseless<&'static str>>,
+    I: Compare<char>,
     <I as Stream>::Token: AsChar + Clone,
     <I as Stream>::IterOffsets: Clone,
     I: AsBStr,
 {
     alt((
-        recognize_float,
-        crate::token::tag_no_case("nan"),
+        take_float,
+        crate::token::literal(Caseless("nan")),
         (
             opt(one_of(['+', '-'])),
-            crate::token::tag_no_case("infinity"),
+            crate::token::literal(Caseless("infinity")),
         )
-            .recognize(),
-        (opt(one_of(['+', '-'])), crate::token::tag_no_case("inf")).recognize(),
+            .take(),
+        (
+            opt(one_of(['+', '-'])),
+            crate::token::literal(Caseless("inf")),
+        )
+            .take(),
     ))
     .parse_next(input)
 }
 
 #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
-fn recognize_float<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+fn take_float<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
 where
     I: StreamIsPartial,
     I: Stream,
-    I: Compare<&'static str>,
+    I: Compare<char>,
     <I as Stream>::Token: AsChar + Clone,
     <I as Stream>::IterOffsets: Clone,
     I: AsBStr,
@@ -1403,20 +1539,29 @@
     (
         opt(one_of(['+', '-'])),
         alt((
-            (digit1, opt(('.', opt(digit1)))).map(|_| ()),
-            ('.', digit1).map(|_| ()),
+            (digit1, opt(('.', opt(digit1)))).void(),
+            ('.', digit1).void(),
         )),
         opt((one_of(['e', 'E']), opt(one_of(['+', '-'])), cut_err(digit1))),
     )
-        .recognize()
+        .take()
         .parse_next(input)
 }
 
-/// Matches a byte string with escaped characters.
+/// Recognize the input slice with escaped characters.
 ///
-/// * The first argument matches the normal characters (it must not accept the control character)
-/// * The second argument is the control character (like `\` in most languages)
-/// * The third argument matches the escaped characters
+/// Arguments:
+/// - `normal`: unescapeable characters
+///   - Must not include `control`
+/// - `control_char`: e.g. `\` for strings in most languages
+/// - `escape`: parse and transform the escaped character
+///
+/// Parsing ends when:
+/// - `alt(normal, control._char)` [`Backtrack`s][crate::error::ErrMode::Backtrack]
+/// - `normal` doesn't advance the input stream
+/// - *(complete)* input stream is exhausted
+///
+/// See also [`escaped_transform`]
 ///
 /// # Example
 ///
@@ -1425,11 +1570,11 @@
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult};
 /// # use winnow::ascii::digit1;
 /// # use winnow::prelude::*;
-/// use winnow::ascii::escaped;
+/// use winnow::ascii::take_escaped;
 /// use winnow::token::one_of;
 ///
 /// fn esc(s: &str) -> IResult<&str, &str> {
-///   escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
+///   take_escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
 /// }
 ///
 /// assert_eq!(esc("123;"), Ok((";", "123")));
@@ -1442,32 +1587,30 @@
 /// # use winnow::ascii::digit1;
 /// # use winnow::prelude::*;
 /// # use winnow::Partial;
-/// use winnow::ascii::escaped;
+/// use winnow::ascii::take_escaped;
 /// use winnow::token::one_of;
 ///
 /// fn esc(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
-///   escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
+///   take_escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
 /// }
 ///
 /// assert_eq!(esc(Partial::new("123;")), Ok((Partial::new(";"), "123")));
 /// assert_eq!(esc(Partial::new("12\\\"34;")), Ok((Partial::new(";"), "12\\\"34")));
 /// ```
 #[inline(always)]
-pub fn escaped<'a, I: 'a, Error, F, G, O1, O2>(
-    mut normal: F,
+pub fn take_escaped<'i, Input, Error, Normal, Escapable, NormalOutput, EscapableOutput>(
+    mut normal: Normal,
     control_char: char,
-    mut escapable: G,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
+    mut escapable: Escapable,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
-    F: Parser<I, O1, Error>,
-    G: Parser<I, O2, Error>,
-    Error: ParserError<I>,
+    Input: StreamIsPartial + Stream + Compare<char> + 'i,
+    Normal: Parser<Input, NormalOutput, Error>,
+    Escapable: Parser<Input, EscapableOutput, Error>,
+    Error: ParserError<Input>,
 {
-    trace("escaped", move |input: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+    trace("take_escaped", move |input: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() && input.is_partial() {
             streaming_escaped_internal(input, &mut normal, control_char, &mut escapable)
         } else {
             complete_escaped_internal(input, &mut normal, control_char, &mut escapable)
@@ -1475,6 +1618,23 @@
     })
 }
 
+/// Deprecated, replaced with [`take_escaped`]
+#[deprecated(since = "0.6.4", note = "Replaced with `take_escaped`")]
+#[inline(always)]
+pub fn escaped<'i, Input, Error, Normal, Escapable, NormalOutput, EscapableOutput>(
+    normal: Normal,
+    control_char: char,
+    escapable: Escapable,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
+where
+    Input: StreamIsPartial + Stream + Compare<char> + 'i,
+    Normal: Parser<Input, NormalOutput, Error>,
+    Escapable: Parser<Input, EscapableOutput, Error>,
+    Error: ParserError<Input>,
+{
+    take_escaped(normal, control_char, escapable)
+}
+
 fn streaming_escaped_internal<I, Error, F, G, O1, O2>(
     input: &mut I,
     normal: &mut F,
@@ -1484,7 +1644,7 @@
 where
     I: StreamIsPartial,
     I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    I: Compare<char>,
     F: Parser<I, O1, Error>,
     G: Parser<I, O2, Error>,
     Error: ParserError<I>,
@@ -1498,7 +1658,7 @@
             Some(_) => {
                 if input.eof_offset() == current_len {
                     let offset = input.offset_from(&start);
-                    input.reset(start);
+                    input.reset(&start);
                     return Ok(input.next_slice(offset));
                 }
             }
@@ -1507,7 +1667,7 @@
                     let _ = escapable.parse_next(input)?;
                 } else {
                     let offset = input.offset_from(&start);
-                    input.reset(start);
+                    input.reset(&start);
                     return Ok(input.next_slice(offset));
                 }
             }
@@ -1517,7 +1677,7 @@
     Err(ErrMode::Incomplete(Needed::Unknown))
 }
 
-fn complete_escaped_internal<'a, I: 'a, Error, F, G, O1, O2>(
+fn complete_escaped_internal<'a, I, Error, F, G, O1, O2>(
     input: &mut I,
     normal: &mut F,
     control_char: char,
@@ -1526,7 +1686,8 @@
 where
     I: StreamIsPartial,
     I: Stream,
-    <I as Stream>::Token: crate::stream::AsChar + Clone,
+    I: Compare<char>,
+    I: 'a,
     F: Parser<I, O1, Error>,
     G: Parser<I, O2, Error>,
     Error: ParserError<I>,
@@ -1540,7 +1701,7 @@
             Some(_) => {
                 if input.eof_offset() == current_len {
                     let offset = input.offset_from(&start);
-                    input.reset(start);
+                    input.reset(&start);
                     return Ok(input.next_slice(offset));
                 }
             }
@@ -1549,32 +1710,38 @@
                     let _ = escapable.parse_next(input)?;
                 } else {
                     let offset = input.offset_from(&start);
-                    input.reset(start);
+                    input.reset(&start);
                     return Ok(input.next_slice(offset));
                 }
             }
         }
     }
 
-    input.reset(start);
+    input.reset(&start);
     Ok(input.finish())
 }
 
-/// Matches a byte string with escaped characters.
+/// Parse escaped characters, unescaping them
 ///
-/// * The first argument matches the normal characters (it must not match the control character)
-/// * The second argument is the control character (like `\` in most languages)
-/// * The third argument matches the escaped characters and transforms them
+/// Arguments:
+/// - `normal`: unescapeable characters
+///   - Must not include `control`
+/// - `control_char`: e.g. `\` for strings in most languages
+/// - `escape`: parse and transform the escaped character
 ///
-/// As an example, the chain `abc\tdef` could be `abc    def` (it also consumes the control character)
+/// Parsing ends when:
+/// - `alt(normal, control._char)` [`Backtrack`s][crate::error::ErrMode::Backtrack]
+/// - `normal` doesn't advance the input stream
+/// - *(complete)* input stream is exhausted
 ///
 /// # Example
 ///
 /// ```rust
+/// # #[cfg(feature = "std")] {
 /// # use winnow::prelude::*;
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
 /// # use std::str::from_utf8;
-/// use winnow::token::tag;
+/// use winnow::token::literal;
 /// use winnow::ascii::escaped_transform;
 /// use winnow::ascii::alpha1;
 /// use winnow::combinator::alt;
@@ -1593,14 +1760,16 @@
 ///
 /// assert_eq!(parser.parse_peek("ab\\\"cd"), Ok(("", String::from("ab\"cd"))));
 /// assert_eq!(parser.parse_peek("ab\\ncd"), Ok(("", String::from("ab\ncd"))));
+/// # }
 /// ```
 ///
 /// ```
+/// # #[cfg(feature = "std")] {
 /// # use winnow::prelude::*;
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
 /// # use std::str::from_utf8;
 /// # use winnow::Partial;
-/// use winnow::token::tag;
+/// use winnow::token::literal;
 /// use winnow::ascii::escaped_transform;
 /// use winnow::ascii::alpha1;
 /// use winnow::combinator::alt;
@@ -1618,27 +1787,26 @@
 /// }
 ///
 /// assert_eq!(parser.parse_peek(Partial::new("ab\\\"cd\"")), Ok((Partial::new("\""), String::from("ab\"cd"))));
+/// # }
 /// ```
 #[inline(always)]
-pub fn escaped_transform<I, Error, F, G, Output>(
-    mut normal: F,
+pub fn escaped_transform<Input, Error, Normal, Escape, Output>(
+    mut normal: Normal,
     control_char: char,
-    mut transform: G,
-) -> impl Parser<I, Output, Error>
+    mut escape: Escape,
+) -> impl Parser<Input, Output, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: crate::stream::AsChar + Clone,
-    Output: crate::stream::Accumulate<<I as Stream>::Slice>,
-    F: Parser<I, <I as Stream>::Slice, Error>,
-    G: Parser<I, <I as Stream>::Slice, Error>,
-    Error: ParserError<I>,
+    Input: StreamIsPartial + Stream + Compare<char>,
+    Output: crate::stream::Accumulate<<Input as Stream>::Slice>,
+    Normal: Parser<Input, <Input as Stream>::Slice, Error>,
+    Escape: Parser<Input, <Input as Stream>::Slice, Error>,
+    Error: ParserError<Input>,
 {
-    trace("escaped_transform", move |input: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
-            streaming_escaped_transform_internal(input, &mut normal, control_char, &mut transform)
+    trace("escaped_transform", move |input: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+            streaming_escaped_transform_internal(input, &mut normal, control_char, &mut escape)
         } else {
-            complete_escaped_transform_internal(input, &mut normal, control_char, &mut transform)
+            complete_escaped_transform_internal(input, &mut normal, control_char, &mut escape)
         }
     })
 }
@@ -1652,7 +1820,7 @@
 where
     I: StreamIsPartial,
     I: Stream,
-    <I as Stream>::Token: crate::stream::AsChar + Clone,
+    I: Compare<char>,
     Output: crate::stream::Accumulate<<I as Stream>::Slice>,
     F: Parser<I, <I as Stream>::Slice, Error>,
     G: Parser<I, <I as Stream>::Slice, Error>,
@@ -1691,7 +1859,7 @@
 where
     I: StreamIsPartial,
     I: Stream,
-    <I as Stream>::Token: crate::stream::AsChar + Clone,
+    I: Compare<char>,
     Output: crate::stream::Accumulate<<I as Stream>::Slice>,
     F: Parser<I, <I as Stream>::Slice, Error>,
     G: Parser<I, <I as Stream>::Slice, Error>,
diff --git a/crates/winnow/src/ascii/tests.rs b/crates/winnow/src/ascii/tests.rs
index 5091dc5..f6891a6 100644
--- a/crates/winnow/src/ascii/tests.rs
+++ b/crates/winnow/src/ascii/tests.rs
@@ -4,7 +4,6 @@
 mod complete {
     use super::*;
     use crate::combinator::alt;
-    use crate::combinator::opt;
     use crate::error::ErrMode;
     use crate::error::ErrorKind;
     use crate::error::InputError;
@@ -427,52 +426,63 @@
         );
     }
 
-    fn digit_to_i16(input: &str) -> IResult<&str, i16> {
-        let i = input;
-        let (i, opt_sign) = opt(alt(('+', '-'))).parse_peek(i)?;
-        let sign = match opt_sign {
-            Some('+') | None => true,
-            Some('-') => false,
-            _ => unreachable!(),
-        };
-
-        let (i, s) = digit1::<_, InputError<_>>.parse_peek(i)?;
-        match s.parse_slice() {
-            Some(n) => {
-                if sign {
-                    Ok((i, n))
-                } else {
-                    Ok((i, -n))
-                }
-            }
-            None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
+    #[test]
+    fn dec_uint_tests() {
+        fn dec_u32(input: &[u8]) -> IResult<&[u8], u32> {
+            dec_uint.parse_peek(input)
         }
+
+        assert_parse!(
+            dec_u32(&b";"[..]),
+            Err(ErrMode::Backtrack(error_position!(
+                &&b";"[..],
+                ErrorKind::Verify
+            )))
+        );
+        assert_parse!(dec_u32(&b"0;"[..]), Ok((&b";"[..], 0)));
+        assert_parse!(dec_u32(&b"1;"[..]), Ok((&b";"[..], 1)));
+        assert_parse!(dec_u32(&b"32;"[..]), Ok((&b";"[..], 32)));
+        assert_parse!(
+            dec_u32(&b"1000000000000000000000;"[..]), // overflow
+            Err(ErrMode::Backtrack(error_position!(
+                &&b"1000000000000000000000;"[..],
+                ErrorKind::Verify
+            )))
+        );
     }
 
-    fn digit_to_u32(i: &str) -> IResult<&str, u32> {
-        let (i, s) = digit1.parse_peek(i)?;
-        match s.parse_slice() {
-            Some(n) => Ok((i, n)),
-            None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
+    #[test]
+    fn dec_int_tests() {
+        fn dec_i32(input: &[u8]) -> IResult<&[u8], i32> {
+            dec_int.parse_peek(input)
         }
-    }
 
-    proptest! {
-      #[test]
-      #[cfg_attr(miri, ignore)]  // See https://github.com/AltSysrq/proptest/issues/253
-      fn ints(s in "\\PC*") {
-          let res1 = digit_to_i16(&s);
-          let res2 = dec_int.parse_peek(s.as_str());
-          assert_eq!(res1, res2);
-      }
-
-      #[test]
-      #[cfg_attr(miri, ignore)]  // See https://github.com/AltSysrq/proptest/issues/253
-      fn uints(s in "\\PC*") {
-          let res1 = digit_to_u32(&s);
-          let res2 = dec_uint.parse_peek(s.as_str());
-          assert_eq!(res1, res2);
-      }
+        assert_parse!(
+            dec_i32(&b";"[..]),
+            Err(ErrMode::Backtrack(error_position!(
+                &&b";"[..],
+                ErrorKind::Verify
+            )))
+        );
+        assert_parse!(dec_i32(&b"0;"[..]), Ok((&b";"[..], 0)));
+        assert_parse!(dec_i32(&b"1;"[..]), Ok((&b";"[..], 1)));
+        assert_parse!(dec_i32(&b"32;"[..]), Ok((&b";"[..], 32)));
+        assert_parse!(
+            dec_i32(&b"-0;"[..]),
+            Err(ErrMode::Backtrack(error_position!(
+                &&b"-0;"[..],
+                ErrorKind::Verify
+            )))
+        );
+        assert_parse!(dec_i32(&b"-1;"[..]), Ok((&b";"[..], -1)));
+        assert_parse!(dec_i32(&b"-32;"[..]), Ok((&b";"[..], -32)));
+        assert_parse!(
+            dec_i32(&b"1000000000000000000000;"[..]), // overflow
+            Err(ErrMode::Backtrack(error_position!(
+                &&b"1000000000000000000000;"[..],
+                ErrorKind::Verify
+            )))
+        );
     }
 
     #[test]
@@ -564,7 +574,7 @@
             let expected32 = str::parse::<f32>(test).unwrap();
             let expected64 = str::parse::<f64>(test).unwrap();
 
-            println!("now parsing: {} -> {}", test, expected32);
+            println!("now parsing: {test} -> {expected32}");
 
             assert_parse!(
                 float.parse_peek(test.as_bytes()),
@@ -588,7 +598,7 @@
         let nan_test_cases = ["nan", "NaN", "NAN"];
 
         for test in nan_test_cases {
-            println!("now parsing: {}", test);
+            println!("now parsing: {test}");
 
             let (remaining, parsed) = float::<_, f32, ()>.parse_peek(test.as_bytes()).unwrap();
             assert!(parsed.is_nan());
@@ -610,7 +620,7 @@
 
     #[cfg(feature = "std")]
     fn parse_f64(i: &str) -> IResult<&str, f64, ()> {
-        match super::recognize_float_or_exceptions.parse_peek(i) {
+        match take_float_or_exceptions.parse_peek(i) {
             Err(e) => Err(e),
             Ok((i, s)) => {
                 if s.is_empty() {
@@ -629,21 +639,21 @@
       #[cfg(feature = "std")]
       #[cfg_attr(miri, ignore)]  // See https://github.com/AltSysrq/proptest/issues/253
       fn floats(s in "\\PC*") {
-          println!("testing {}", s);
+          println!("testing {s}");
           let res1 = parse_f64(&s);
           let res2 = float::<_, f64, ()>.parse_peek(s.as_str());
           assert_eq!(res1, res2);
       }
     }
 
-    // issue #1336 "escaped hangs if normal parser accepts empty"
+    // issue #1336 "take_escaped hangs if normal parser accepts empty"
     #[test]
-    fn complete_escaped_hang() {
-        // issue #1336 "escaped hangs if normal parser accepts empty"
+    fn complete_take_escaped_hang() {
+        // issue #1336 "take_escaped hangs if normal parser accepts empty"
         fn escaped_string(input: &str) -> IResult<&str, &str> {
             use crate::ascii::alpha0;
             use crate::token::one_of;
-            escaped(alpha0, '\\', one_of(['n'])).parse_peek(input)
+            take_escaped(alpha0, '\\', one_of(['n'])).parse_peek(input)
         }
 
         escaped_string("7").unwrap();
@@ -651,8 +661,8 @@
     }
 
     #[test]
-    fn complete_escaped_hang_1118() {
-        // issue ##1118 escaped does not work with empty string
+    fn complete_take_escaped_hang_1118() {
+        // issue ##1118 take_escaped does not work with empty string
         fn unquote(input: &str) -> IResult<&str, &str> {
             use crate::combinator::delimited;
             use crate::combinator::opt;
@@ -660,7 +670,7 @@
 
             delimited(
                 '"',
-                escaped(
+                take_escaped(
                     opt(none_of(['\\', '"'])),
                     '\\',
                     one_of(['\\', '"', 'r', 'n', 't']),
@@ -681,7 +691,7 @@
         use crate::token::one_of;
 
         fn esc(i: &[u8]) -> IResult<&[u8], &[u8]> {
-            escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+            take_escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
         }
         assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], &b"abcd"[..])));
         assert_eq!(esc(&b"ab\\\"cd;"[..]), Ok((&b";"[..], &b"ab\\\"cd"[..])));
@@ -705,7 +715,7 @@
         );
 
         fn esc2(i: &[u8]) -> IResult<&[u8], &[u8]> {
-            escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+            take_escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
         }
         assert_eq!(esc2(&b"12\\nnn34"[..]), Ok((&b"nn34"[..], &b"12\\n"[..])));
     }
@@ -717,7 +727,7 @@
         use crate::token::one_of;
 
         fn esc(i: &str) -> IResult<&str, &str> {
-            escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+            take_escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
         }
         assert_eq!(esc("abcd;"), Ok((";", "abcd")));
         assert_eq!(esc("ab\\\"cd;"), Ok((";", "ab\\\"cd")));
@@ -738,12 +748,12 @@
         );
 
         fn esc2(i: &str) -> IResult<&str, &str> {
-            escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+            take_escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
         }
         assert_eq!(esc2("12\\nnn34"), Ok(("nn34", "12\\n")));
 
         fn esc3(i: &str) -> IResult<&str, &str> {
-            escaped(alpha, '\u{241b}', one_of(['\"', 'n'])).parse_peek(i)
+            take_escaped(alpha, '\u{241b}', one_of(['\"', 'n'])).parse_peek(i)
         }
         assert_eq!(esc3("ab␛ncd;"), Ok((";", "ab␛ncd")));
     }
@@ -752,7 +762,7 @@
     fn test_escaped_error() {
         fn esc(s: &str) -> IResult<&str, &str> {
             use crate::ascii::digit1;
-            escaped(digit1, '\\', one_of(['\"', 'n', '\\'])).parse_peek(s)
+            take_escaped(digit1, '\\', one_of(['\"', 'n', '\\'])).parse_peek(s)
         }
 
         assert_eq!(esc("abcd"), Ok(("abcd", "")));
@@ -900,14 +910,11 @@
 
 mod partial {
     use super::*;
-    use crate::combinator::opt;
     use crate::error::ErrorKind;
     use crate::error::InputError;
     use crate::error::{ErrMode, Needed};
-    use crate::stream::ParseSlice;
     use crate::IResult;
     use crate::Partial;
-    use proptest::prelude::*;
 
     macro_rules! assert_parse(
     ($left: expr, $right: expr) => {
@@ -1243,7 +1250,7 @@
         let d: &[u8] = b"ab12cd";
         assert_eq!(
             till_line_ending::<_, InputError<_>>.parse_peek(Partial::new(d)),
-            Err(ErrMode::Incomplete(Needed::new(1)))
+            Err(ErrMode::Incomplete(Needed::Unknown))
         );
     }
 
@@ -1261,7 +1268,7 @@
         let g2: &str = "ab12cd";
         assert_eq!(
             till_line_ending::<_, InputError<_>>.parse_peek(Partial::new(g2)),
-            Err(ErrMode::Incomplete(Needed::new(1)))
+            Err(ErrMode::Incomplete(Needed::Unknown))
         );
     }
 
@@ -1454,54 +1461,6 @@
         );
     }
 
-    fn digit_to_i16(input: Partial<&str>) -> IResult<Partial<&str>, i16> {
-        let i = input;
-        let (i, opt_sign) = opt(one_of(['+', '-'])).parse_peek(i)?;
-        let sign = match opt_sign {
-            Some('+') | None => true,
-            Some('-') => false,
-            _ => unreachable!(),
-        };
-
-        let (i, s) = digit1::<_, InputError<_>>.parse_peek(i)?;
-        match s.parse_slice() {
-            Some(n) => {
-                if sign {
-                    Ok((i, n))
-                } else {
-                    Ok((i, -n))
-                }
-            }
-            None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
-        }
-    }
-
-    fn digit_to_u32(i: Partial<&str>) -> IResult<Partial<&str>, u32> {
-        let (i, s) = digit1.parse_peek(i)?;
-        match s.parse_slice() {
-            Some(n) => Ok((i, n)),
-            None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
-        }
-    }
-
-    proptest! {
-      #[test]
-      #[cfg_attr(miri, ignore)]  // See https://github.com/AltSysrq/proptest/issues/253
-      fn ints(s in "\\PC*") {
-          let res1 = digit_to_i16(Partial::new(&s));
-          let res2 = dec_int.parse_peek(Partial::new(s.as_str()));
-          assert_eq!(res1, res2);
-      }
-
-      #[test]
-      #[cfg_attr(miri, ignore)]  // See https://github.com/AltSysrq/proptest/issues/253
-      fn uints(s in "\\PC*") {
-          let res1 = digit_to_u32(Partial::new(&s));
-          let res2 = dec_uint.parse_peek(Partial::new(s.as_str()));
-          assert_eq!(res1, res2);
-      }
-    }
-
     #[test]
     fn hex_uint_tests() {
         fn hex_u32(input: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
diff --git a/crates/winnow/src/binary/bits/mod.rs b/crates/winnow/src/binary/bits/mod.rs
index 4a198c4..9c8e037 100644
--- a/crates/winnow/src/binary/bits/mod.rs
+++ b/crates/winnow/src/binary/bits/mod.rs
@@ -7,8 +7,8 @@
 use crate::combinator::trace;
 use crate::error::{ErrMode, ErrorConvert, ErrorKind, Needed, ParserError};
 use crate::lib::std::ops::{AddAssign, Div, Shl, Shr};
-use crate::stream::{AsBytes, Stream, StreamIsPartial, ToUsize};
-use crate::{unpeek, IResult, PResult, Parser};
+use crate::stream::{Stream, StreamIsPartial, ToUsize};
+use crate::{PResult, Parser};
 
 /// Number of bits in a byte
 const BYTE: usize = u8::BITS as usize;
@@ -46,45 +46,51 @@
 /// assert_eq!(parsed.0, 0x01);
 /// assert_eq!(parsed.1, 0x23);
 /// ```
-pub fn bits<I, O, E1, E2, P>(mut parser: P) -> impl Parser<I, O, E2>
+pub fn bits<Input, Output, BitError, ByteError, ParseNext>(
+    mut parser: ParseNext,
+) -> impl Parser<Input, Output, ByteError>
 where
-    E1: ParserError<(I, usize)> + ErrorConvert<E2>,
-    E2: ParserError<I>,
-    I: Stream + Clone,
-    P: Parser<(I, usize), O, E1>,
+    BitError: ParserError<(Input, usize)> + ErrorConvert<ByteError>,
+    ByteError: ParserError<Input>,
+    (Input, usize): Stream,
+    Input: Stream + Clone,
+    ParseNext: Parser<(Input, usize), Output, BitError>,
 {
-    trace(
-        "bits",
-        unpeek(move |input: I| {
-            match parser.parse_peek((input, 0)) {
-                Ok(((rest, offset), result)) => {
-                    // If the next byte has been partially read, it will be sliced away as well.
-                    // The parser functions might already slice away all fully read bytes.
-                    // That's why `offset / BYTE` isn't necessarily needed at all times.
-                    let remaining_bytes_index =
-                        offset / BYTE + if offset % BYTE == 0 { 0 } else { 1 };
-                    let (input, _) = rest.peek_slice(remaining_bytes_index);
-                    Ok((input, result))
-                }
-                Err(ErrMode::Incomplete(n)) => {
-                    Err(ErrMode::Incomplete(n.map(|u| u.get() / BYTE + 1)))
-                }
-                Err(e) => Err(e.convert()),
+    trace("bits", move |input: &mut Input| {
+        let mut bit_input = (input.clone(), 0);
+        match parser.parse_next(&mut bit_input) {
+            Ok(result) => {
+                let (mut rest, offset) = bit_input;
+                // If the next byte has been partially read, it will be sliced away as well.
+                // The parser functions might already slice away all fully read bytes.
+                // That's why `offset / BYTE` isn't necessarily needed at all times.
+                let remaining_bytes_index = offset / BYTE + if offset % BYTE == 0 { 0 } else { 1 };
+                let _ = rest.next_slice(remaining_bytes_index);
+                *input = rest;
+                Ok(result)
             }
-        }),
-    )
+            Err(ErrMode::Incomplete(n)) => Err(ErrMode::Incomplete(n.map(|u| u.get() / BYTE + 1))),
+            Err(e) => Err(e.convert()),
+        }
+    })
 }
 
 /// Convert a [`bits`] stream back into a byte stream
 ///
+/// <div class="warning">
+///
 /// **Warning:** A partial byte remaining in the input will be ignored and the given parser will
 /// start parsing at the next full byte.
 ///
+/// </div>
+///
+/// # Examples
+///
 /// ```
 /// use winnow::prelude::*;
 /// use winnow::Bytes;
 /// use winnow::binary::bits::{bits, bytes, take};
-/// use winnow::combinator::rest;
+/// use winnow::token::rest;
 /// use winnow::error::InputError;
 ///
 /// type Stream<'i> = &'i Bytes;
@@ -105,44 +111,54 @@
 ///
 /// assert_eq!(parse(input), Ok(( stream(&[]), (0x01, 0x23, &[0xff, 0xff][..]) )));
 /// ```
-pub fn bytes<I, O, E1, E2, P>(mut parser: P) -> impl Parser<(I, usize), O, E2>
+pub fn bytes<Input, Output, ByteError, BitError, ParseNext>(
+    mut parser: ParseNext,
+) -> impl Parser<(Input, usize), Output, BitError>
 where
-    E1: ParserError<I> + ErrorConvert<E2>,
-    E2: ParserError<(I, usize)>,
-    I: Stream<Token = u8> + Clone,
-    P: Parser<I, O, E1>,
+    ByteError: ParserError<Input> + ErrorConvert<BitError>,
+    BitError: ParserError<(Input, usize)>,
+    Input: Stream<Token = u8> + Clone,
+    ParseNext: Parser<Input, Output, ByteError>,
 {
-    trace(
-        "bytes",
-        unpeek(move |(input, offset): (I, usize)| {
-            let (inner, _) = if offset % BYTE != 0 {
-                input.peek_slice(1 + offset / BYTE)
-            } else {
-                input.peek_slice(offset / BYTE)
-            };
-            let i = (input, offset);
-            match parser.parse_peek(inner) {
-                Ok((rest, res)) => Ok(((rest, 0), res)),
-                Err(ErrMode::Incomplete(Needed::Unknown)) => {
-                    Err(ErrMode::Incomplete(Needed::Unknown))
-                }
-                Err(ErrMode::Incomplete(Needed::Size(sz))) => {
-                    Err(match sz.get().checked_mul(BYTE) {
-                        Some(v) => ErrMode::Incomplete(Needed::new(v)),
-                        None => ErrMode::Cut(E2::assert(
-                            &i,
-                            "overflow in turning needed bytes into needed bits",
-                        )),
-                    })
-                }
-                Err(e) => Err(e.convert()),
+    trace("bytes", move |bit_input: &mut (Input, usize)| {
+        let (mut input, offset) = bit_input.clone();
+        let _ = if offset % BYTE != 0 {
+            input.next_slice(1 + offset / BYTE)
+        } else {
+            input.next_slice(offset / BYTE)
+        };
+        match parser.parse_next(&mut input) {
+            Ok(res) => {
+                *bit_input = (input, 0);
+                Ok(res)
             }
-        }),
-    )
+            Err(ErrMode::Incomplete(Needed::Unknown)) => Err(ErrMode::Incomplete(Needed::Unknown)),
+            Err(ErrMode::Incomplete(Needed::Size(sz))) => Err(match sz.get().checked_mul(BYTE) {
+                Some(v) => ErrMode::Incomplete(Needed::new(v)),
+                None => ErrMode::Cut(BitError::assert(
+                    bit_input,
+                    "overflow in turning needed bytes into needed bits",
+                )),
+            }),
+            Err(e) => Err(e.convert()),
+        }
+    })
 }
 
 /// Parse taking `count` bits
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `(&[u8], usize)` bit [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// # use winnow::error::ContextError;
+/// pub fn take<'i>(count: usize) -> impl Parser<(&'i [u8], usize), u8, ContextError>
+/// # {
+/// #     winnow::binary::bits::take(count)
+/// # }
+/// ```
+///
 /// # Example
 /// ```rust
 /// # use winnow::prelude::*;
@@ -173,37 +189,36 @@
 /// assert_eq!(parser((stream(&[0b00010010]), 0), 12), Err(winnow::error::ErrMode::Backtrack(InputError::new((stream(&[0b00010010]), 0), ErrorKind::Eof))));
 /// ```
 #[inline(always)]
-pub fn take<I, O, C, E: ParserError<(I, usize)>>(count: C) -> impl Parser<(I, usize), O, E>
+pub fn take<Input, Output, Count, Error>(count: Count) -> impl Parser<(Input, usize), Output, Error>
 where
-    I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone,
-    C: ToUsize,
-    O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>,
+    Input: Stream<Token = u8> + StreamIsPartial + Clone,
+    Output: From<u8> + AddAssign + Shl<usize, Output = Output> + Shr<usize, Output = Output>,
+    Count: ToUsize,
+    Error: ParserError<(Input, usize)>,
 {
     let count = count.to_usize();
-    trace(
-        "take",
-        unpeek(move |input: (I, usize)| {
-            if <I as StreamIsPartial>::is_partial_supported() {
-                take_::<_, _, _, true>(input, count)
-            } else {
-                take_::<_, _, _, false>(input, count)
-            }
-        }),
-    )
+    trace("take", move |input: &mut (Input, usize)| {
+        if <Input as StreamIsPartial>::is_partial_supported() {
+            take_::<_, _, _, true>(input, count)
+        } else {
+            take_::<_, _, _, false>(input, count)
+        }
+    })
 }
 
 fn take_<I, O, E: ParserError<(I, usize)>, const PARTIAL: bool>(
-    (input, bit_offset): (I, usize),
+    bit_input: &mut (I, usize),
     count: usize,
-) -> IResult<(I, usize), O, E>
+) -> PResult<O, E>
 where
     I: StreamIsPartial,
-    I: Stream<Token = u8> + AsBytes + Clone,
+    I: Stream<Token = u8> + Clone,
     O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>,
 {
     if count == 0 {
-        Ok(((input, bit_offset), 0u8.into()))
+        Ok(0u8.into())
     } else {
+        let (mut input, bit_offset) = bit_input.clone();
         if input.eof_offset() * BYTE < count + bit_offset {
             if PARTIAL && input.is_partial() {
                 Err(ErrMode::Incomplete(Needed::new(count)))
@@ -220,7 +235,7 @@
             let mut remaining: usize = count;
             let mut end_offset: usize = 0;
 
-            for byte in input.as_bytes().iter().copied().take(cnt + 1) {
+            for (_, byte) in input.iter_offsets().take(cnt + 1) {
                 if remaining == 0 {
                     break;
                 }
@@ -240,21 +255,34 @@
                     offset = 0;
                 }
             }
-            let (input, _) = input.peek_slice(cnt);
-            Ok(((input, end_offset), acc))
+            let _ = input.next_slice(cnt);
+            *bit_input = (input, end_offset);
+            Ok(acc)
         }
     }
 }
 
 /// Parse taking `count` bits and comparing them to `pattern`
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `(&[u8], usize)` bit [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// # use winnow::error::ContextError;
+/// pub fn pattern<'i>(pattern: u8, count: usize) -> impl Parser<(&'i [u8], usize), u8, ContextError>
+/// # {
+/// #     winnow::binary::bits::pattern(pattern, count)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```rust
 /// # use winnow::prelude::*;
 /// # use winnow::Bytes;
 /// # use winnow::error::{InputError, ErrorKind};
-/// use winnow::binary::bits::tag;
+/// use winnow::binary::bits::pattern;
 ///
 /// type Stream<'i> = &'i Bytes;
 ///
@@ -265,8 +293,8 @@
 /// /// Compare the lowest `count` bits of `input` against the lowest `count` bits of `pattern`.
 /// /// Return Ok and the matching section of `input` if there's a match.
 /// /// Return Err if there's no match.
-/// fn parser(pattern: u8, count: u8, input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), u8> {
-///     tag(pattern, count).parse_peek(input)
+/// fn parser(bits: u8, count: u8, input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), u8> {
+///     pattern(bits, count).parse_peek(input)
 /// }
 ///
 /// // The lowest 4 bits of 0b00001111 match the lowest 4 bits of 0b11111111.
@@ -302,25 +330,30 @@
 #[inline(always)]
 #[doc(alias = "literal")]
 #[doc(alias = "just")]
-pub fn tag<I, O, C, E: ParserError<(I, usize)>>(
-    pattern: O,
-    count: C,
-) -> impl Parser<(I, usize), O, E>
+#[doc(alias = "tag")]
+pub fn pattern<Input, Output, Count, Error: ParserError<(Input, usize)>>(
+    pattern: Output,
+    count: Count,
+) -> impl Parser<(Input, usize), Output, Error>
 where
-    I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone,
-    C: ToUsize,
-    O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O> + PartialEq,
+    Input: Stream<Token = u8> + StreamIsPartial + Clone,
+    Count: ToUsize,
+    Output: From<u8>
+        + AddAssign
+        + Shl<usize, Output = Output>
+        + Shr<usize, Output = Output>
+        + PartialEq,
 {
     let count = count.to_usize();
-    trace("tag", move |input: &mut (I, usize)| {
+    trace("pattern", move |input: &mut (Input, usize)| {
         let start = input.checkpoint();
 
         take(count).parse_next(input).and_then(|o| {
             if pattern == o {
                 Ok(o)
             } else {
-                input.reset(start);
-                Err(ErrMode::Backtrack(E::from_error_kind(
+                input.reset(&start);
+                Err(ErrMode::Backtrack(Error::from_error_kind(
                     input,
                     ErrorKind::Tag,
                 )))
@@ -331,6 +364,18 @@
 
 /// Parses one specific bit as a bool.
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `(&[u8], usize)` bit [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// # use winnow::error::ContextError;
+/// pub fn bool(input: &mut (&[u8], usize)) -> PResult<bool>
+/// # {
+/// #     winnow::binary::bits::bool.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```rust
@@ -353,11 +398,13 @@
 /// assert_eq!(parse((stream(&[0b10000000]), 1)), Ok(((stream(&[0b10000000]), 2), false)));
 /// ```
 #[doc(alias = "any")]
-pub fn bool<I, E: ParserError<(I, usize)>>(input: &mut (I, usize)) -> PResult<bool, E>
+pub fn bool<Input, Error: ParserError<(Input, usize)>>(
+    input: &mut (Input, usize),
+) -> PResult<bool, Error>
 where
-    I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone,
+    Input: Stream<Token = u8> + StreamIsPartial + Clone,
 {
-    trace("bool", |input: &mut (I, usize)| {
+    trace("bool", |input: &mut (Input, usize)| {
         let bit: u32 = take(1usize).parse_next(input)?;
         Ok(bit != 0)
     })
diff --git a/crates/winnow/src/binary/bits/tests.rs b/crates/winnow/src/binary/bits/tests.rs
index 41207c6..630b451 100644
--- a/crates/winnow/src/binary/bits/tests.rs
+++ b/crates/winnow/src/binary/bits/tests.rs
@@ -1,4 +1,5 @@
 use super::*;
+use crate::error::IResult;
 use crate::error::InputError;
 use crate::Partial;
 
@@ -59,7 +60,7 @@
 
     assert!(result.is_err());
     let error = result.err().unwrap();
-    assert_eq!("Parsing requires 2 bytes/chars", error.to_string());
+    assert_eq!("Parsing requires 2 more data", error.to_string());
 }
 
 #[test]
@@ -69,7 +70,7 @@
     assert_eq!(count, 0usize);
     let offset = 0usize;
 
-    let result: crate::IResult<(&[u8], usize), usize> = take(count).parse_peek((input, offset));
+    let result: IResult<(&[u8], usize), usize> = take(count).parse_peek((input, offset));
 
     assert_eq!(result, Ok(((input, offset), 0)));
 }
@@ -78,7 +79,7 @@
 fn test_take_complete_eof() {
     let input = &[0b00010010][..];
 
-    let result: crate::IResult<(&[u8], usize), usize> = take(1usize).parse_peek((input, 8));
+    let result: IResult<(&[u8], usize), usize> = take(1usize).parse_peek((input, 8));
 
     assert_eq!(
         result,
@@ -93,7 +94,7 @@
 fn test_take_complete_span_over_multiple_bytes() {
     let input = &[0b00010010, 0b00110100, 0b11111111, 0b11111111][..];
 
-    let result: crate::IResult<(&[u8], usize), usize> = take(24usize).parse_peek((input, 4));
+    let result: IResult<(&[u8], usize), usize> = take(24usize).parse_peek((input, 4));
 
     assert_eq!(
         result,
@@ -108,33 +109,33 @@
     assert_eq!(count, 0usize);
     let offset = 0usize;
 
-    let result: crate::IResult<(_, usize), usize> = take(count).parse_peek((input, offset));
+    let result: IResult<(_, usize), usize> = take(count).parse_peek((input, offset));
 
     assert_eq!(result, Ok(((input, offset), 0)));
 }
 
 #[test]
-fn test_tag_partial_ok() {
+fn test_pattern_partial_ok() {
     let input = Partial::new(&[0b00011111][..]);
     let offset = 0usize;
     let bits_to_take = 4usize;
-    let value_to_tag = 0b0001;
+    let value_to_pattern = 0b0001;
 
-    let result: crate::IResult<(_, usize), usize> =
-        tag(value_to_tag, bits_to_take).parse_peek((input, offset));
+    let result: IResult<(_, usize), usize> =
+        pattern(value_to_pattern, bits_to_take).parse_peek((input, offset));
 
-    assert_eq!(result, Ok(((input, bits_to_take), value_to_tag)));
+    assert_eq!(result, Ok(((input, bits_to_take), value_to_pattern)));
 }
 
 #[test]
-fn test_tag_partial_err() {
+fn test_pattern_partial_err() {
     let input = Partial::new(&[0b00011111][..]);
     let offset = 0usize;
     let bits_to_take = 4usize;
-    let value_to_tag = 0b1111;
+    let value_to_pattern = 0b1111;
 
-    let result: crate::IResult<(_, usize), usize> =
-        tag(value_to_tag, bits_to_take).parse_peek((input, offset));
+    let result: IResult<(_, usize), usize> =
+        pattern(value_to_pattern, bits_to_take).parse_peek((input, offset));
 
     assert_eq!(
         result,
@@ -149,7 +150,7 @@
 fn test_bool_0_complete() {
     let input = [0b10000000].as_ref();
 
-    let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 0));
+    let result: IResult<(&[u8], usize), bool> = bool.parse_peek((input, 0));
 
     assert_eq!(result, Ok(((input, 1), true)));
 }
@@ -158,7 +159,7 @@
 fn test_bool_eof_complete() {
     let input = [0b10000000].as_ref();
 
-    let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 8));
+    let result: IResult<(&[u8], usize), bool> = bool.parse_peek((input, 8));
 
     assert_eq!(
         result,
@@ -173,7 +174,7 @@
 fn test_bool_0_partial() {
     let input = Partial::new([0b10000000].as_ref());
 
-    let result: crate::IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 0));
+    let result: IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 0));
 
     assert_eq!(result, Ok(((input, 1), true)));
 }
@@ -182,7 +183,7 @@
 fn test_bool_eof_partial() {
     let input = Partial::new([0b10000000].as_ref());
 
-    let result: crate::IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 8));
+    let result: IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 8));
 
     assert_eq!(
         result,
diff --git a/crates/winnow/src/binary/mod.rs b/crates/winnow/src/binary/mod.rs
index 01053a5..bc1c656 100644
--- a/crates/winnow/src/binary/mod.rs
+++ b/crates/winnow/src/binary/mod.rs
@@ -15,9 +15,8 @@
 use crate::error::ParserError;
 use crate::lib::std::ops::{Add, Shl};
 use crate::stream::Accumulate;
-use crate::stream::{AsBytes, Stream, StreamIsPartial};
+use crate::stream::{Stream, StreamIsPartial};
 use crate::stream::{ToUsize, UpdateSlice};
-use crate::token::take;
 use crate::PResult;
 use crate::Parser;
 
@@ -36,7 +35,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -68,10 +67,10 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn be_u8<I, E: ParserError<I>>(input: &mut I) -> PResult<u8, E>
+pub fn be_u8<Input, Error>(input: &mut Input) -> PResult<u8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
     u8(input)
 }
@@ -80,7 +79,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -112,20 +111,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn be_u16<I, E: ParserError<I>>(input: &mut I) -> PResult<u16, E>
+pub fn be_u16<Input, Error>(input: &mut Input) -> PResult<u16, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_u16", move |input: &mut I| be_uint(input, 2)).parse_next(input)
+    trace("be_u16", move |input: &mut Input| be_uint(input, 2)).parse_next(input)
 }
 
 /// Recognizes a big endian unsigned 3 byte integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -157,20 +155,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn be_u24<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+pub fn be_u24<Input, Error>(input: &mut Input) -> PResult<u32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_u23", move |input: &mut I| be_uint(input, 3)).parse_next(input)
+    trace("be_u23", move |input: &mut Input| be_uint(input, 3)).parse_next(input)
 }
 
 /// Recognizes a big endian unsigned 4 bytes integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -202,20 +199,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn be_u32<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+pub fn be_u32<Input, Error>(input: &mut Input) -> PResult<u32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_u32", move |input: &mut I| be_uint(input, 4)).parse_next(input)
+    trace("be_u32", move |input: &mut Input| be_uint(input, 4)).parse_next(input)
 }
 
 /// Recognizes a big endian unsigned 8 bytes integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -247,20 +243,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn be_u64<I, E: ParserError<I>>(input: &mut I) -> PResult<u64, E>
+pub fn be_u64<Input, Error>(input: &mut Input) -> PResult<u64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_u64", move |input: &mut I| be_uint(input, 8)).parse_next(input)
+    trace("be_u64", move |input: &mut Input| be_uint(input, 8)).parse_next(input)
 }
 
 /// Recognizes a big endian unsigned 16 bytes integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -292,36 +287,46 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
 /// ```
 #[inline(always)]
-pub fn be_u128<I, E: ParserError<I>>(input: &mut I) -> PResult<u128, E>
+pub fn be_u128<Input, Error>(input: &mut Input) -> PResult<u128, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_u128", move |input: &mut I| be_uint(input, 16)).parse_next(input)
+    trace("be_u128", move |input: &mut Input| be_uint(input, 16)).parse_next(input)
 }
 
 #[inline]
-fn be_uint<I, Uint, E: ParserError<I>>(input: &mut I, bound: usize) -> PResult<Uint, E>
+fn be_uint<Input, Uint, Error>(input: &mut Input, bound: usize) -> PResult<Uint, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
     Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+    Error: ParserError<Input>,
 {
     debug_assert_ne!(bound, 1, "to_be_uint needs extra work to avoid overflow");
-    take(bound)
-        .map(|n: <I as Stream>::Slice| to_be_uint(n.as_bytes()))
-        .parse_next(input)
+    match input.offset_at(bound) {
+        Ok(offset) => {
+            let res = to_be_uint(input, offset);
+            input.next_slice(offset);
+            Ok(res)
+        }
+        Err(e) if <Input as StreamIsPartial>::is_partial_supported() && input.is_partial() => {
+            Err(ErrMode::Incomplete(e))
+        }
+        Err(_needed) => Err(ErrMode::from_error_kind(input, ErrorKind::Slice)),
+    }
 }
 
 #[inline]
-fn to_be_uint<Uint>(number: &[u8]) -> Uint
+fn to_be_uint<Input, Uint>(number: &Input, offset: usize) -> Uint
 where
-    Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+    Input: Stream,
+    Uint: Default
+        + Shl<u8, Output = Uint>
+        + Add<Uint, Output = Uint>
+        + From<<Input as Stream>::Token>,
 {
     let mut res = Uint::default();
-    for byte in number.iter().copied() {
+    for (_, byte) in number.iter_offsets().take(offset) {
         res = (res << 8) + byte.into();
     }
 
@@ -332,7 +337,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -364,10 +369,10 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn be_i8<I, E: ParserError<I>>(input: &mut I) -> PResult<i8, E>
+pub fn be_i8<Input, Error>(input: &mut Input) -> PResult<i8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
     i8(input)
 }
@@ -376,7 +381,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -408,13 +413,12 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn be_i16<I, E: ParserError<I>>(input: &mut I) -> PResult<i16, E>
+pub fn be_i16<Input, Error>(input: &mut Input) -> PResult<i16, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_i16", move |input: &mut I| {
+    trace("be_i16", move |input: &mut Input| {
         be_uint::<_, u16, _>(input, 2).map(|n| n as i16)
     })
     .parse_next(input)
@@ -424,7 +428,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -456,13 +460,12 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn be_i24<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+pub fn be_i24<Input, Error>(input: &mut Input) -> PResult<i32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_i24", move |input: &mut I| {
+    trace("be_i24", move |input: &mut Input| {
         be_uint::<_, u32, _>(input, 3).map(|n| {
             // Same as the unsigned version but we need to sign-extend manually here
             let n = if n & 0x80_00_00 != 0 {
@@ -480,7 +483,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -512,13 +515,12 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(4))));
 /// ```
 #[inline(always)]
-pub fn be_i32<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+pub fn be_i32<Input, Error>(input: &mut Input) -> PResult<i32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_i32", move |input: &mut I| {
+    trace("be_i32", move |input: &mut Input| {
         be_uint::<_, u32, _>(input, 4).map(|n| n as i32)
     })
     .parse_next(input)
@@ -528,7 +530,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -560,13 +562,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn be_i64<I, E: ParserError<I>>(input: &mut I) -> PResult<i64, E>
+pub fn be_i64<Input, Error>(input: &mut Input) -> PResult<i64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_i64", move |input: &mut I| {
+    trace("be_i64", move |input: &mut Input| {
         be_uint::<_, u64, _>(input, 8).map(|n| n as i64)
     })
     .parse_next(input)
@@ -576,7 +577,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -608,13 +609,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
 /// ```
 #[inline(always)]
-pub fn be_i128<I, E: ParserError<I>>(input: &mut I) -> PResult<i128, E>
+pub fn be_i128<Input, Error>(input: &mut Input) -> PResult<i128, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_i128", move |input: &mut I| {
+    trace("be_i128", move |input: &mut Input| {
         be_uint::<_, u128, _>(input, 16).map(|n| n as i128)
     })
     .parse_next(input)
@@ -624,7 +624,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -656,10 +656,10 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn le_u8<I, E: ParserError<I>>(input: &mut I) -> PResult<u8, E>
+pub fn le_u8<Input, Error>(input: &mut Input) -> PResult<u8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
     u8(input)
 }
@@ -668,7 +668,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -700,20 +700,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn le_u16<I, E: ParserError<I>>(input: &mut I) -> PResult<u16, E>
+pub fn le_u16<Input, Error>(input: &mut Input) -> PResult<u16, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_u16", move |input: &mut I| le_uint(input, 2)).parse_next(input)
+    trace("le_u16", move |input: &mut Input| le_uint(input, 2)).parse_next(input)
 }
 
 /// Recognizes a little endian unsigned 3 byte integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -745,20 +744,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn le_u24<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+pub fn le_u24<Input, Error>(input: &mut Input) -> PResult<u32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_u24", move |input: &mut I| le_uint(input, 3)).parse_next(input)
+    trace("le_u24", move |input: &mut Input| le_uint(input, 3)).parse_next(input)
 }
 
 /// Recognizes a little endian unsigned 4 bytes integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -790,20 +788,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn le_u32<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+pub fn le_u32<Input, Error>(input: &mut Input) -> PResult<u32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_u32", move |input: &mut I| le_uint(input, 4)).parse_next(input)
+    trace("le_u32", move |input: &mut Input| le_uint(input, 4)).parse_next(input)
 }
 
 /// Recognizes a little endian unsigned 8 bytes integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -835,20 +832,19 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn le_u64<I, E: ParserError<I>>(input: &mut I) -> PResult<u64, E>
+pub fn le_u64<Input, Error>(input: &mut Input) -> PResult<u64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_u64", move |input: &mut I| le_uint(input, 8)).parse_next(input)
+    trace("le_u64", move |input: &mut Input| le_uint(input, 8)).parse_next(input)
 }
 
 /// Recognizes a little endian unsigned 16 bytes integer.
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -880,35 +876,45 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
 /// ```
 #[inline(always)]
-pub fn le_u128<I, E: ParserError<I>>(input: &mut I) -> PResult<u128, E>
+pub fn le_u128<Input, Error>(input: &mut Input) -> PResult<u128, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_u128", move |input: &mut I| le_uint(input, 16)).parse_next(input)
+    trace("le_u128", move |input: &mut Input| le_uint(input, 16)).parse_next(input)
 }
 
 #[inline]
-fn le_uint<I, Uint, E: ParserError<I>>(input: &mut I, bound: usize) -> PResult<Uint, E>
+fn le_uint<Input, Uint, Error>(input: &mut Input, bound: usize) -> PResult<Uint, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
     Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+    Error: ParserError<Input>,
 {
-    take(bound)
-        .map(|n: <I as Stream>::Slice| to_le_uint(n.as_bytes()))
-        .parse_next(input)
+    match input.offset_at(bound) {
+        Ok(offset) => {
+            let res = to_le_uint(input, offset);
+            input.next_slice(offset);
+            Ok(res)
+        }
+        Err(e) if <Input as StreamIsPartial>::is_partial_supported() && input.is_partial() => {
+            Err(ErrMode::Incomplete(e))
+        }
+        Err(_needed) => Err(ErrMode::from_error_kind(input, ErrorKind::Slice)),
+    }
 }
 
 #[inline]
-fn to_le_uint<Uint>(number: &[u8]) -> Uint
+fn to_le_uint<Input, Uint>(number: &Input, offset: usize) -> Uint
 where
-    Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+    Input: Stream,
+    Uint: Default
+        + Shl<u8, Output = Uint>
+        + Add<Uint, Output = Uint>
+        + From<<Input as Stream>::Token>,
 {
     let mut res = Uint::default();
-    for (index, byte) in number.iter_offsets() {
+    for (index, byte) in number.iter_offsets().take(offset) {
         res = res + (Uint::from(byte) << (8 * index as u8));
     }
 
@@ -919,7 +925,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -951,10 +957,10 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn le_i8<I, E: ParserError<I>>(input: &mut I) -> PResult<i8, E>
+pub fn le_i8<Input, Error>(input: &mut Input) -> PResult<i8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
     i8(input)
 }
@@ -963,7 +969,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -995,13 +1001,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn le_i16<I, E: ParserError<I>>(input: &mut I) -> PResult<i16, E>
+pub fn le_i16<Input, Error>(input: &mut Input) -> PResult<i16, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_i16", move |input: &mut I| {
+    trace("le_i16", move |input: &mut Input| {
         le_uint::<_, u16, _>(input, 2).map(|n| n as i16)
     })
     .parse_next(input)
@@ -1011,7 +1016,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1043,13 +1048,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn le_i24<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+pub fn le_i24<Input, Error>(input: &mut Input) -> PResult<i32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_i24", move |input: &mut I| {
+    trace("le_i24", move |input: &mut Input| {
         le_uint::<_, u32, _>(input, 3).map(|n| {
             // Same as the unsigned version but we need to sign-extend manually here
             let n = if n & 0x80_00_00 != 0 {
@@ -1067,7 +1071,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1099,13 +1103,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn le_i32<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+pub fn le_i32<Input, Error>(input: &mut Input) -> PResult<i32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_i32", move |input: &mut I| {
+    trace("le_i32", move |input: &mut Input| {
         le_uint::<_, u32, _>(input, 4).map(|n| n as i32)
     })
     .parse_next(input)
@@ -1115,7 +1118,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1147,13 +1150,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn le_i64<I, E: ParserError<I>>(input: &mut I) -> PResult<i64, E>
+pub fn le_i64<Input, Error>(input: &mut Input) -> PResult<i64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_i64", move |input: &mut I| {
+    trace("le_i64", move |input: &mut Input| {
         le_uint::<_, u64, _>(input, 8).map(|n| n as i64)
     })
     .parse_next(input)
@@ -1163,7 +1165,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1195,13 +1197,12 @@
 /// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
 /// ```
 #[inline(always)]
-pub fn le_i128<I, E: ParserError<I>>(input: &mut I) -> PResult<i128, E>
+pub fn le_i128<Input, Error>(input: &mut Input) -> PResult<i128, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_i128", move |input: &mut I| {
+    trace("le_i128", move |input: &mut Input| {
         le_uint::<_, u128, _>(input, 16).map(|n| n as i128)
     })
     .parse_next(input)
@@ -1209,11 +1210,15 @@
 
 /// Recognizes an unsigned 1 byte integer
 ///
+/// <div class="warning">
+///
 /// **Note:** that endianness does not apply to 1 byte numbers.
 ///
+/// </div>
+///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1246,13 +1251,13 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn u8<I, E: ParserError<I>>(input: &mut I) -> PResult<u8, E>
+pub fn u8<Input, Error>(input: &mut Input) -> PResult<u8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("u8", move |input: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() {
+    trace("u8", move |input: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() {
             u8_::<_, _, true>(input)
         } else {
             u8_::<_, _, false>(input)
@@ -1261,16 +1266,16 @@
     .parse_next(input)
 }
 
-fn u8_<I, E: ParserError<I>, const PARTIAL: bool>(input: &mut I) -> PResult<u8, E>
+fn u8_<Input, Error, const PARTIAL: bool>(input: &mut Input) -> PResult<u8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
     input.next_token().ok_or_else(|| {
         if PARTIAL && input.is_partial() {
             ErrMode::Incomplete(Needed::new(1))
         } else {
-            ErrMode::Backtrack(E::from_error_kind(input, ErrorKind::Token))
+            ErrMode::Backtrack(Error::from_error_kind(input, ErrorKind::Token))
         }
     })
 }
@@ -1282,7 +1287,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1329,13 +1334,12 @@
 /// assert_eq!(le_u16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn u16<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u16, E>
+pub fn u16<Input, Error>(endian: Endianness) -> impl Parser<Input, u16, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_u16,
             Endianness::Little => le_u16,
@@ -1354,7 +1358,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1401,13 +1405,12 @@
 /// assert_eq!(le_u24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn u24<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u32, E>
+pub fn u24<Input, Error>(endian: Endianness) -> impl Parser<Input, u32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_u24,
             Endianness::Little => le_u24,
@@ -1426,7 +1429,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1473,13 +1476,12 @@
 /// assert_eq!(le_u32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn u32<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u32, E>
+pub fn u32<Input, Error>(endian: Endianness) -> impl Parser<Input, u32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_u32,
             Endianness::Little => le_u32,
@@ -1498,7 +1500,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1545,13 +1547,12 @@
 /// assert_eq!(le_u64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn u64<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u64, E>
+pub fn u64<Input, Error>(endian: Endianness) -> impl Parser<Input, u64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_u64,
             Endianness::Little => le_u64,
@@ -1570,7 +1571,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1617,13 +1618,12 @@
 /// assert_eq!(le_u128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
 /// ```
 #[inline(always)]
-pub fn u128<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u128, E>
+pub fn u128<Input, Error>(endian: Endianness) -> impl Parser<Input, u128, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_u128,
             Endianness::Little => le_u128,
@@ -1637,11 +1637,15 @@
 
 /// Recognizes a signed 1 byte integer
 ///
+/// <div class="warning">
+///
 /// **Note:** that endianness does not apply to 1 byte numbers.
 ///
+/// </div>
+///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1674,13 +1678,13 @@
 /// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn i8<I, E: ParserError<I>>(input: &mut I) -> PResult<i8, E>
+pub fn i8<Input, Error>(input: &mut Input) -> PResult<i8, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("i8", move |input: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() {
+    trace("i8", move |input: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() {
             u8_::<_, _, true>(input)
         } else {
             u8_::<_, _, false>(input)
@@ -1697,7 +1701,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1744,13 +1748,12 @@
 /// assert_eq!(le_i16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn i16<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i16, E>
+pub fn i16<Input, Error>(endian: Endianness) -> impl Parser<Input, i16, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_i16,
             Endianness::Little => le_i16,
@@ -1769,7 +1772,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1816,13 +1819,12 @@
 /// assert_eq!(le_i24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
 #[inline(always)]
-pub fn i24<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i32, E>
+pub fn i24<Input, Error>(endian: Endianness) -> impl Parser<Input, i32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_i24,
             Endianness::Little => le_i24,
@@ -1841,7 +1843,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1888,13 +1890,12 @@
 /// assert_eq!(le_i32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn i32<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i32, E>
+pub fn i32<Input, Error>(endian: Endianness) -> impl Parser<Input, i32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_i32,
             Endianness::Little => le_i32,
@@ -1913,7 +1914,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -1960,13 +1961,12 @@
 /// assert_eq!(le_i64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn i64<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i64, E>
+pub fn i64<Input, Error>(endian: Endianness) -> impl Parser<Input, i64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_i64,
             Endianness::Little => le_i64,
@@ -1985,7 +1985,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2032,13 +2032,12 @@
 /// assert_eq!(le_i128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
 /// ```
 #[inline(always)]
-pub fn i128<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i128, E>
+pub fn i128<Input, Error>(endian: Endianness) -> impl Parser<Input, i128, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_i128,
             Endianness::Little => le_i128,
@@ -2054,7 +2053,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2087,13 +2086,12 @@
 /// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn be_f32<I, E: ParserError<I>>(input: &mut I) -> PResult<f32, E>
+pub fn be_f32<Input, Error>(input: &mut Input) -> PResult<f32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_f32", move |input: &mut I| {
+    trace("be_f32", move |input: &mut Input| {
         be_uint::<_, u32, _>(input, 4).map(f32::from_bits)
     })
     .parse_next(input)
@@ -2103,7 +2101,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2135,13 +2133,12 @@
 /// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn be_f64<I, E: ParserError<I>>(input: &mut I) -> PResult<f64, E>
+pub fn be_f64<Input, Error>(input: &mut Input) -> PResult<f64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_f64", move |input: &mut I| {
+    trace("be_f64", move |input: &mut Input| {
         be_uint::<_, u64, _>(input, 8).map(f64::from_bits)
     })
     .parse_next(input)
@@ -2151,7 +2148,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2183,13 +2180,12 @@
 /// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(3))));
 /// ```
 #[inline(always)]
-pub fn le_f32<I, E: ParserError<I>>(input: &mut I) -> PResult<f32, E>
+pub fn le_f32<Input, Error>(input: &mut Input) -> PResult<f32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("le_f32", move |input: &mut I| {
+    trace("le_f32", move |input: &mut Input| {
         le_uint::<_, u32, _>(input, 4).map(f32::from_bits)
     })
     .parse_next(input)
@@ -2199,7 +2195,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2231,13 +2227,12 @@
 /// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(7))));
 /// ```
 #[inline(always)]
-pub fn le_f64<I, E: ParserError<I>>(input: &mut I) -> PResult<f64, E>
+pub fn le_f64<Input, Error>(input: &mut Input) -> PResult<f64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    trace("be_f64", move |input: &mut I| {
+    trace("be_f64", move |input: &mut Input| {
         le_uint::<_, u64, _>(input, 8).map(f64::from_bits)
     })
     .parse_next(input)
@@ -2250,7 +2245,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2297,13 +2292,12 @@
 /// assert_eq!(le_f32(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn f32<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, f32, E>
+pub fn f32<Input, Error>(endian: Endianness) -> impl Parser<Input, f32, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_f32,
             Endianness::Little => le_f32,
@@ -2322,7 +2316,7 @@
 ///
 /// *Complete version*: returns an error if there is not enough input data
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2369,13 +2363,12 @@
 /// assert_eq!(le_f64(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(5))));
 /// ```
 #[inline(always)]
-pub fn f64<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, f64, E>
+pub fn f64<Input, Error>(endian: Endianness) -> impl Parser<Input, f64, Error>
 where
-    I: StreamIsPartial,
-    I: Stream<Token = u8>,
-    <I as Stream>::Slice: AsBytes,
+    Input: StreamIsPartial + Stream<Token = u8>,
+    Error: ParserError<Input>,
 {
-    move |input: &mut I| {
+    move |input: &mut Input| {
         match endian {
             Endianness::Big => be_f64,
             Endianness::Little => le_f64,
@@ -2395,10 +2388,7 @@
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
-///
-/// # Arguments
-/// * `f` The parser to apply.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2408,7 +2398,6 @@
 /// use winnow::Bytes;
 /// use winnow::binary::be_u16;
 /// use winnow::binary::length_take;
-/// use winnow::token::tag;
 ///
 /// type Stream<'i> = Partial<&'i Bytes>;
 ///
@@ -2423,43 +2412,27 @@
 /// assert_eq!(parser(stream(b"\x00\x03abcefg")), Ok((stream(&b"efg"[..]), &b"abc"[..])));
 /// assert_eq!(parser(stream(b"\x00\x03a")), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
-pub fn length_take<I, N, E, F>(mut f: F) -> impl Parser<I, <I as Stream>::Slice, E>
+pub fn length_take<Input, Count, Error, CountParser>(
+    mut count: CountParser,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    N: ToUsize,
-    F: Parser<I, N, E>,
-    E: ParserError<I>,
+    Input: StreamIsPartial + Stream,
+    Count: ToUsize,
+    CountParser: Parser<Input, Count, Error>,
+    Error: ParserError<Input>,
 {
-    trace("length_take", move |i: &mut I| {
-        let length = f.parse_next(i)?;
+    trace("length_take", move |i: &mut Input| {
+        let length = count.parse_next(i)?;
 
         crate::token::take(length).parse_next(i)
     })
 }
 
-/// Deprecated since 0.5.27, replaced with [`length_take`]
-#[deprecated(since = "0.5.27", note = "Replaced with `length_take`")]
-pub fn length_data<I, N, E, F>(f: F) -> impl Parser<I, <I as Stream>::Slice, E>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    N: ToUsize,
-    F: Parser<I, N, E>,
-    E: ParserError<I>,
-{
-    length_take(f)
-}
-
 /// Parse a length-prefixed slice ([TLV](https://en.wikipedia.org/wiki/Type-length-value))
 ///
 /// *Complete version*: Returns an error if there is not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
-///
-/// # Arguments
-/// * `f` The parser to apply.
-/// * `g` The parser to apply on the subslice.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
 ///
 /// # Example
 ///
@@ -2469,7 +2442,6 @@
 /// use winnow::Bytes;
 /// use winnow::binary::be_u16;
 /// use winnow::binary::length_and_then;
-/// use winnow::token::tag;
 ///
 /// type Stream<'i> = Partial<&'i Bytes>;
 ///
@@ -2491,46 +2463,30 @@
 /// assert_eq!(parser(stream(b"\x00\x03123123")), Err(ErrMode::Backtrack(InputError::new(complete_stream(&b"123"[..]), ErrorKind::Tag))));
 /// assert_eq!(parser(stream(b"\x00\x03a")), Err(ErrMode::Incomplete(Needed::new(2))));
 /// ```
-pub fn length_and_then<I, O, N, E, F, G>(mut f: F, mut g: G) -> impl Parser<I, O, E>
+pub fn length_and_then<Input, Output, Count, Error, CountParser, ParseNext>(
+    mut count: CountParser,
+    mut parser: ParseNext,
+) -> impl Parser<Input, Output, Error>
 where
-    I: StreamIsPartial,
-    I: Stream + UpdateSlice + Clone,
-    N: ToUsize,
-    F: Parser<I, N, E>,
-    G: Parser<I, O, E>,
-    E: ParserError<I>,
+    Input: StreamIsPartial + Stream + UpdateSlice + Clone,
+    Count: ToUsize,
+    CountParser: Parser<Input, Count, Error>,
+    ParseNext: Parser<Input, Output, Error>,
+    Error: ParserError<Input>,
 {
-    trace("length_and_then", move |i: &mut I| {
-        let data = length_take(f.by_ref()).parse_next(i)?;
-        let mut data = I::update_slice(i.clone(), data);
+    trace("length_and_then", move |i: &mut Input| {
+        let data = length_take(count.by_ref()).parse_next(i)?;
+        let mut data = Input::update_slice(i.clone(), data);
         let _ = data.complete();
-        let o = g.by_ref().complete_err().parse_next(&mut data)?;
+        let o = parser.by_ref().complete_err().parse_next(&mut data)?;
         Ok(o)
     })
 }
 
-/// Deprecated since 0.5.27, replaced with [`length_and_then`]
-#[deprecated(since = "0.5.27", note = "Replaced with `length_and_then`")]
-pub fn length_value<I, O, N, E, F, G>(f: F, g: G) -> impl Parser<I, O, E>
-where
-    I: StreamIsPartial,
-    I: Stream + UpdateSlice + Clone,
-    N: ToUsize,
-    F: Parser<I, N, E>,
-    G: Parser<I, O, E>,
-    E: ParserError<I>,
-{
-    length_and_then(f, g)
-}
-
 /// [`Accumulate`] a length-prefixed sequence of values ([TLV](https://en.wikipedia.org/wiki/Type-length-value))
 ///
 /// If the length represents token counts, see instead [`length_take`]
 ///
-/// # Arguments
-/// * `f` The parser to apply to obtain the count.
-/// * `g` The parser to apply repeatedly.
-///
 /// # Example
 ///
 /// ```rust
@@ -2540,8 +2496,7 @@
 /// # use winnow::prelude::*;
 /// use winnow::Bytes;
 /// use winnow::binary::u8;
-/// use winnow::binary::length_count;
-/// use winnow::token::tag;
+/// use winnow::binary::length_repeat;
 ///
 /// type Stream<'i> = &'i Bytes;
 ///
@@ -2550,7 +2505,7 @@
 /// }
 ///
 /// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, Vec<&[u8]>> {
-///   length_count(u8.map(|i| {
+///   length_repeat(u8.map(|i| {
 ///      println!("got number: {}", i);
 ///      i
 ///   }), "abc").parse_peek(s)
@@ -2560,32 +2515,21 @@
 /// assert_eq!(parser(stream(b"\x03123123123")), Err(ErrMode::Backtrack(InputError::new(stream(b"123123123"), ErrorKind::Tag))));
 /// # }
 /// ```
-pub fn length_repeat<I, O, C, N, E, F, G>(mut f: F, mut g: G) -> impl Parser<I, C, E>
+pub fn length_repeat<Input, Output, Accumulator, Count, Error, CountParser, ParseNext>(
+    mut count: CountParser,
+    mut parser: ParseNext,
+) -> impl Parser<Input, Accumulator, Error>
 where
-    I: Stream,
-    N: ToUsize,
-    C: Accumulate<O>,
-    F: Parser<I, N, E>,
-    G: Parser<I, O, E>,
-    E: ParserError<I>,
+    Input: Stream,
+    Count: ToUsize,
+    Accumulator: Accumulate<Output>,
+    CountParser: Parser<Input, Count, Error>,
+    ParseNext: Parser<Input, Output, Error>,
+    Error: ParserError<Input>,
 {
-    trace("length_repeat", move |i: &mut I| {
-        let n = f.parse_next(i)?;
+    trace("length_repeat", move |i: &mut Input| {
+        let n = count.parse_next(i)?;
         let n = n.to_usize();
-        repeat(n, g.by_ref()).parse_next(i)
+        repeat(n, parser.by_ref()).parse_next(i)
     })
 }
-
-/// Deprecated since 0.5.27, replaced with [`length_repeat`]
-#[deprecated(since = "0.5.27", note = "Replaced with `length_repeat`")]
-pub fn length_count<I, O, C, N, E, F, G>(f: F, g: G) -> impl Parser<I, C, E>
-where
-    I: Stream,
-    N: ToUsize,
-    C: Accumulate<O>,
-    F: Parser<I, N, E>,
-    G: Parser<I, O, E>,
-    E: ParserError<I>,
-{
-    length_repeat(f, g)
-}
diff --git a/crates/winnow/src/binary/tests.rs b/crates/winnow/src/binary/tests.rs
index bc2a005..9747b36 100644
--- a/crates/winnow/src/binary/tests.rs
+++ b/crates/winnow/src/binary/tests.rs
@@ -797,6 +797,22 @@
     }
 
     #[test]
+    fn le_u16_tests() {
+        assert_parse!(
+            le_u16.parse_peek(Partial::new(&[0x00, 0x03][..])),
+            Ok((Partial::new(&b""[..]), 0x0300))
+        );
+        assert_parse!(
+            le_u16.parse_peek(Partial::new(&[b'a', b'b'][..])),
+            Ok((Partial::new(&b""[..]), 0x6261))
+        );
+        assert_parse!(
+            le_u16.parse_peek(Partial::new(&[0x01][..])),
+            Err(ErrMode::Incomplete(Needed::new(1)))
+        );
+    }
+
+    #[test]
     fn le_i8_tests() {
         assert_parse!(
             le_i8.parse_peek(Partial::new(&[0x00][..])),
@@ -869,6 +885,22 @@
     }
 
     #[test]
+    fn le_u32_test() {
+        assert_parse!(
+            le_u32.parse_peek(Partial::new(&[0x00, 0x03, 0x05, 0x07][..])),
+            Ok((Partial::new(&b""[..]), 0x07050300))
+        );
+        assert_parse!(
+            le_u32.parse_peek(Partial::new(&[b'a', b'b', b'c', b'd'][..])),
+            Ok((Partial::new(&b""[..]), 0x64636261))
+        );
+        assert_parse!(
+            le_u32.parse_peek(Partial::new(&[0x01][..])),
+            Err(ErrMode::Incomplete(Needed::new(3)))
+        );
+    }
+
+    #[test]
     fn le_i32_tests() {
         assert_parse!(
             le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
diff --git a/crates/winnow/src/combinator/branch.rs b/crates/winnow/src/combinator/branch.rs
index 7fdcf8d..535b438 100644
--- a/crates/winnow/src/combinator/branch.rs
+++ b/crates/winnow/src/combinator/branch.rs
@@ -6,7 +6,7 @@
 #[doc(inline)]
 pub use crate::dispatch;
 
-/// Helper trait for the [alt()] combinator.
+/// Helper trait for the [`alt()`] combinator.
 ///
 /// This trait is implemented for tuples of up to 21 elements
 pub trait Alt<I, O, E> {
@@ -17,7 +17,7 @@
 /// Pick the first successful parser
 ///
 /// To stop on an error, rather than trying further cases, see
-/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_6]).
+/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]).
 ///
 /// For tight control over the error when no match is found, add a final case using [`fail`][crate::combinator::fail].
 /// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all
@@ -37,7 +37,7 @@
 ///   alt((alpha1, digit1)).parse_peek(input)
 /// };
 ///
-/// // the first parser, alpha1, recognizes the input
+/// // the first parser, alpha1, takes the input
 /// assert_eq!(parser("abc"), Ok(("", "abc")));
 ///
 /// // the first parser returns an error, so alt tries the second one
@@ -48,13 +48,18 @@
 /// # }
 /// ```
 #[doc(alias = "choice")]
-pub fn alt<I: Stream, O, E: ParserError<I>, List: Alt<I, O, E>>(
-    mut l: List,
-) -> impl Parser<I, O, E> {
-    trace("alt", move |i: &mut I| l.choice(i))
+#[inline(always)]
+pub fn alt<Input: Stream, Output, Error, Alternatives>(
+    mut alternatives: Alternatives,
+) -> impl Parser<Input, Output, Error>
+where
+    Alternatives: Alt<Input, Output, Error>,
+    Error: ParserError<Input>,
+{
+    trace("alt", move |i: &mut Input| alternatives.choice(i))
 }
 
-/// Helper trait for the [permutation()] combinator.
+/// Helper trait for the [`permutation()`] combinator.
 ///
 /// This trait is implemented for tuples of up to 21 elements
 pub trait Permutation<I, O, E> {
@@ -69,7 +74,7 @@
 /// tuple of the parser results.
 ///
 /// To stop on an error, rather than trying further permutations, see
-/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_6]).
+/// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]).
 ///
 /// # Example
 ///
@@ -83,7 +88,7 @@
 ///   permutation((alpha1, digit1)).parse_peek(input)
 /// }
 ///
-/// // permutation recognizes alphabetic characters then digit
+/// // permutation takes alphabetic characters then digit
 /// assert_eq!(parser("abc123"), Ok(("", ("abc", "123"))));
 ///
 /// // but also in inverse order
@@ -111,9 +116,10 @@
 ///
 /// // any parses 'a', then char('a') fails on 'b',
 /// // even though char('a') followed by any would succeed
-/// assert_eq!(parser("ab"), Err(ErrMode::Backtrack(InputError::new("b", ErrorKind::Verify))));
+/// assert_eq!(parser("ab"), Err(ErrMode::Backtrack(InputError::new("b", ErrorKind::Tag))));
 /// ```
 ///
+#[inline(always)]
 pub fn permutation<I: Stream, O, E: ParserError<I>, List: Permutation<I, O, E>>(
     mut l: List,
 ) -> impl Parser<I, O, E> {
@@ -126,7 +132,7 @@
 
         let start = input.checkpoint();
         for branch in self {
-            input.reset(start.clone());
+            input.reset(&start);
             match branch.parse_next(input) {
                 Err(ErrMode::Backtrack(e)) => {
                     error = match error {
@@ -139,7 +145,32 @@
         }
 
         match error {
-            Some(e) => Err(ErrMode::Backtrack(e.append(input, ErrorKind::Alt))),
+            Some(e) => Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Alt))),
+            None => Err(ErrMode::assert(input, "`alt` needs at least one parser")),
+        }
+    }
+}
+
+impl<I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for &mut [P] {
+    fn choice(&mut self, input: &mut I) -> PResult<O, E> {
+        let mut error: Option<E> = None;
+
+        let start = input.checkpoint();
+        for branch in self.iter_mut() {
+            input.reset(&start);
+            match branch.parse_next(input) {
+                Err(ErrMode::Backtrack(e)) => {
+                    error = match error {
+                        Some(error) => Some(error.or(e)),
+                        None => Some(e),
+                    };
+                }
+                res => return res,
+            }
+        }
+
+        match error {
+            Some(e) => Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Alt))),
             None => Err(ErrMode::assert(input, "`alt` needs at least one parser")),
         }
     }
@@ -204,7 +235,7 @@
 
 macro_rules! alt_trait_inner(
   ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({
-    $input.reset($start.clone());
+    $input.reset(&$start);
     match $self.$it.parse_next($input) {
       Err(ErrMode::Backtrack(e)) => {
         let err = $err.or(e);
@@ -214,14 +245,14 @@
     }
   });
   ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({
-    Err(ErrMode::Backtrack($err.append($input, ErrorKind::Alt)))
+    Err(ErrMode::Backtrack($err.append($input, &$start, ErrorKind::Alt)))
   });
 );
 
 alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10 Alt11 Alt12 Alt13 Alt14 Alt15 Alt16 Alt17 Alt18 Alt19 Alt20 Alt21 Alt22);
 
 // Manually implement Alt for (A,), the 1-tuple type
-impl<I, O, E: ParserError<I>, A: Parser<I, O, E>> Alt<I, O, E> for (A,) {
+impl<I: Stream, O, E: ParserError<I>, A: Parser<I, O, E>> Alt<I, O, E> for (A,) {
     fn choice(&mut self, input: &mut I) -> PResult<O, E> {
         self.0.parse_next(input)
     }
@@ -266,8 +297,8 @@
           // or errored on the remaining input
           if let Some(err) = err {
             // There are remaining parsers, and all errored on the remaining input
-            input.reset(start.clone());
-            return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Alt)));
+            input.reset(&start);
+            return Err(ErrMode::Backtrack(err.append(input, &start, ErrorKind::Alt)));
           }
 
           // All parsers were applied
@@ -284,7 +315,7 @@
 macro_rules! permutation_trait_inner(
   ($it:tt, $self:expr, $input:ident, $start:ident, $res:expr, $err:expr, $head:ident $($id:ident)*) => (
     if $res.$it.is_none() {
-      $input.reset($start.clone());
+      $input.reset(&$start);
       match $self.$it.parse_next($input) {
         Ok(o) => {
           $res.$it = Some(o);
diff --git a/crates/winnow/src/combinator/core.rs b/crates/winnow/src/combinator/core.rs
index efd7758..3a2020d 100644
--- a/crates/winnow/src/combinator/core.rs
+++ b/crates/winnow/src/combinator/core.rs
@@ -3,50 +3,26 @@
 use crate::stream::Stream;
 use crate::*;
 
-/// Return the remaining input.
-///
-/// # Example
-///
-/// ```rust
-/// # use winnow::prelude::*;
-/// # use winnow::error::ErrorKind;
-/// # use winnow::error::InputError;
-/// use winnow::combinator::rest;
-/// assert_eq!(rest::<_,InputError<_>>.parse_peek("abc"), Ok(("", "abc")));
-/// assert_eq!(rest::<_,InputError<_>>.parse_peek(""), Ok(("", "")));
-/// ```
+/// Deprecated, replaced with [`token::rest`]
+#[deprecated(since = "0.6.23", note = "replaced with `token::rest`")]
 #[inline]
-pub fn rest<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn rest<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: Stream,
+    Input: Stream,
+    Error: ParserError<Input>,
 {
-    trace("rest", move |input: &mut I| Ok(input.finish())).parse_next(input)
+    crate::token::rest(input)
 }
 
-/// Return the length of the remaining input.
-///
-/// Note: this does not advance the [`Stream`]
-///
-/// # Example
-///
-/// ```rust
-/// # use winnow::prelude::*;
-/// # use winnow::error::ErrorKind;
-/// # use winnow::error::InputError;
-/// use winnow::combinator::rest_len;
-/// assert_eq!(rest_len::<_,InputError<_>>.parse_peek("abc"), Ok(("abc", 3)));
-/// assert_eq!(rest_len::<_,InputError<_>>.parse_peek(""), Ok(("", 0)));
-/// ```
+/// Deprecated, replaced with [`token::rest_len`]
+#[deprecated(since = "0.6.23", note = "replaced with `token::rest_len`")]
 #[inline]
-pub fn rest_len<I, E: ParserError<I>>(input: &mut I) -> PResult<usize, E>
+pub fn rest_len<Input, Error>(input: &mut Input) -> PResult<usize, Error>
 where
-    I: Stream,
+    Input: Stream,
+    Error: ParserError<Input>,
 {
-    trace("rest_len", move |input: &mut I| {
-        let len = input.eof_offset();
-        Ok(len)
-    })
-    .parse_next(input)
+    crate::token::rest_len(input)
 }
 
 /// Apply a [`Parser`], producing `None` on [`ErrMode::Backtrack`].
@@ -70,16 +46,19 @@
 /// assert_eq!(parser("123;"), Ok(("123;", None)));
 /// # }
 /// ```
-pub fn opt<I: Stream, O, E: ParserError<I>, F>(mut f: F) -> impl Parser<I, Option<O>, E>
+pub fn opt<Input: Stream, Output, Error, ParseNext>(
+    mut parser: ParseNext,
+) -> impl Parser<Input, Option<Output>, Error>
 where
-    F: Parser<I, O, E>,
+    ParseNext: Parser<Input, Output, Error>,
+    Error: ParserError<Input>,
 {
-    trace("opt", move |input: &mut I| {
+    trace("opt", move |input: &mut Input| {
         let start = input.checkpoint();
-        match f.parse_next(input) {
+        match parser.parse_next(input) {
             Ok(o) => Ok(Some(o)),
             Err(ErrMode::Backtrack(_)) => {
-                input.reset(start);
+                input.reset(&start);
                 Ok(None)
             }
             Err(e) => Err(e),
@@ -108,21 +87,27 @@
 /// assert_eq!(parser(false, "123;"), Ok(("123;", None)));
 /// # }
 /// ```
-pub fn cond<I, O, E: ParserError<I>, F>(b: bool, mut f: F) -> impl Parser<I, Option<O>, E>
+pub fn cond<Input, Output, Error, ParseNext>(
+    cond: bool,
+    mut parser: ParseNext,
+) -> impl Parser<Input, Option<Output>, Error>
 where
-    I: Stream,
-    F: Parser<I, O, E>,
+    Input: Stream,
+    ParseNext: Parser<Input, Output, Error>,
+    Error: ParserError<Input>,
 {
-    trace("cond", move |input: &mut I| {
-        if b {
-            f.parse_next(input).map(Some)
+    trace("cond", move |input: &mut Input| {
+        if cond {
+            parser.parse_next(input).map(Some)
         } else {
             Ok(None)
         }
     })
 }
 
-/// Tries to apply its parser without consuming the input.
+/// Apply the parser without advancing the input.
+///
+/// To lookahead and only advance on success, see [`opt`].
 ///
 /// # Example
 ///
@@ -141,14 +126,18 @@
 /// ```
 #[doc(alias = "look_ahead")]
 #[doc(alias = "rewind")]
-pub fn peek<I: Stream, O, E: ParserError<I>, F>(mut f: F) -> impl Parser<I, O, E>
+pub fn peek<Input, Output, Error, ParseNext>(
+    mut parser: ParseNext,
+) -> impl Parser<Input, Output, Error>
 where
-    F: Parser<I, O, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    ParseNext: Parser<Input, Output, Error>,
 {
-    trace("peek", move |input: &mut I| {
+    trace("peek", move |input: &mut Input| {
         let start = input.checkpoint();
-        let res = f.parse_next(input);
-        input.reset(start);
+        let res = parser.parse_next(input);
+        input.reset(&start);
         res
     })
 }
@@ -157,6 +146,17 @@
 ///
 /// Otherwise, it will error.
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn eof<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::combinator::eof.parse_next(input)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```rust
@@ -171,11 +171,12 @@
 /// ```
 #[doc(alias = "end")]
 #[doc(alias = "eoi")]
-pub fn eof<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+pub fn eof<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
 where
-    I: Stream,
+    Input: Stream,
+    Error: ParserError<Input>,
 {
-    trace("eof", move |input: &mut I| {
+    trace("eof", move |input: &mut Input| {
         if input.eof_offset() == 0 {
             Ok(input.next_slice(0))
         } else {
@@ -187,8 +188,12 @@
 
 /// Succeeds if the child parser returns an error.
 ///
+/// <div class="warning">
+///
 /// **Note:** This does not advance the [`Stream`]
 ///
+/// </div>
+///
 /// # Example
 ///
 /// ```rust
@@ -204,14 +209,16 @@
 /// assert_eq!(parser.parse_peek("abcd"), Err(ErrMode::Backtrack(InputError::new("abcd", ErrorKind::Not))));
 /// # }
 /// ```
-pub fn not<I: Stream, O, E: ParserError<I>, F>(mut parser: F) -> impl Parser<I, (), E>
+pub fn not<Input, Output, Error, ParseNext>(mut parser: ParseNext) -> impl Parser<Input, (), Error>
 where
-    F: Parser<I, O, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    ParseNext: Parser<Input, Output, Error>,
 {
-    trace("not", move |input: &mut I| {
+    trace("not", move |input: &mut Input| {
         let start = input.checkpoint();
         let res = parser.parse_next(input);
-        input.reset(start);
+        input.reset(&start);
         match res {
             Ok(_) => Err(ErrMode::from_error_kind(input, ErrorKind::Not)),
             Err(ErrMode::Backtrack(_)) => Ok(()),
@@ -225,7 +232,7 @@
 /// This commits the parse result, preventing alternative branch paths like with
 /// [`winnow::combinator::alt`][crate::combinator::alt].
 ///
-/// See the [tutorial][crate::_tutorial::chapter_6] for more details.
+/// See the [tutorial][crate::_tutorial::chapter_7] for more details.
 ///
 /// # Example
 ///
@@ -233,8 +240,8 @@
 /// ```rust
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
 /// # use winnow::token::one_of;
+/// # use winnow::token::rest;
 /// # use winnow::ascii::digit1;
-/// # use winnow::combinator::rest;
 /// # use winnow::combinator::alt;
 /// # use winnow::combinator::preceded;
 /// # use winnow::prelude::*;
@@ -258,8 +265,8 @@
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
 /// # use winnow::prelude::*;
 /// # use winnow::token::one_of;
+/// # use winnow::token::rest;
 /// # use winnow::ascii::digit1;
-/// # use winnow::combinator::rest;
 /// # use winnow::combinator::alt;
 /// # use winnow::combinator::preceded;
 /// use winnow::combinator::cut_err;
@@ -277,12 +284,15 @@
 /// assert_eq!(parser("+"), Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice ))));
 /// # }
 /// ```
-pub fn cut_err<I, O, E: ParserError<I>, F>(mut parser: F) -> impl Parser<I, O, E>
+pub fn cut_err<Input, Output, Error, ParseNext>(
+    mut parser: ParseNext,
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    F: Parser<I, O, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    ParseNext: Parser<Input, Output, Error>,
 {
-    trace("cut_err", move |input: &mut I| {
+    trace("cut_err", move |input: &mut Input| {
         parser.parse_next(input).map_err(|e| e.cut())
     })
 }
@@ -291,12 +301,15 @@
 ///
 /// This attempts the parse, allowing other parsers to be tried on failure, like with
 /// [`winnow::combinator::alt`][crate::combinator::alt].
-pub fn backtrack_err<I, O, E: ParserError<I>, F>(mut parser: F) -> impl Parser<I, O, E>
+pub fn backtrack_err<Input, Output, Error, ParseNext>(
+    mut parser: ParseNext,
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    F: Parser<I, O, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    ParseNext: Parser<Input, Output, Error>,
 {
-    trace("backtrack_err", move |input: &mut I| {
+    trace("backtrack_err", move |input: &mut Input| {
         parser.parse_next(input).map_err(|e| e.backtrack())
     })
 }
@@ -320,12 +333,15 @@
 /// }
 /// ```
 #[track_caller]
-pub fn todo<I, O, E>(input: &mut I) -> PResult<O, E>
+pub fn todo<Input, Output, Error>(input: &mut Input) -> PResult<Output, Error>
 where
-    I: Stream,
+    Input: Stream,
 {
     #![allow(clippy::todo)]
-    trace("todo", move |_input: &mut I| todo!("unimplemented parse")).parse_next(input)
+    trace("todo", move |_input: &mut Input| {
+        todo!("unimplemented parse")
+    })
+    .parse_next(input)
 }
 
 /// Repeats the embedded parser, lazily returning the results
@@ -338,7 +354,7 @@
 /// # Example
 ///
 /// ```rust
-/// use winnow::{combinator::iterator, IResult, token::tag, ascii::alpha1, combinator::terminated};
+/// use winnow::{combinator::iterator, IResult, ascii::alpha1, combinator::terminated};
 /// use std::collections::HashMap;
 ///
 /// let data = "abc|defg|hijkl|mnopqr|123";
@@ -350,11 +366,14 @@
 /// assert_eq!(parsed, [("abc", 3usize), ("defg", 4), ("hijkl", 5), ("mnopqr", 6)].iter().cloned().collect());
 /// assert_eq!(res, Ok(("123", ())));
 /// ```
-pub fn iterator<I, O, E, F>(input: I, parser: F) -> ParserIterator<F, I, O, E>
+pub fn iterator<Input, Output, Error, ParseNext>(
+    input: Input,
+    parser: ParseNext,
+) -> ParserIterator<ParseNext, Input, Output, Error>
 where
-    F: Parser<I, O, E>,
-    I: Stream,
-    E: ParserError<I>,
+    ParseNext: Parser<Input, Output, Error>,
+    Input: Stream,
+    Error: ParserError<Input>,
 {
     ParserIterator {
         parser,
@@ -391,7 +410,7 @@
     }
 }
 
-impl<'a, F, I, O, E> core::iter::Iterator for &'a mut ParserIterator<F, I, O, E>
+impl<F, I, O, E> core::iter::Iterator for &mut ParserIterator<F, I, O, E>
 where
     F: Parser<I, O, E>,
     I: Stream,
@@ -408,7 +427,7 @@
                     Some(o)
                 }
                 Err(ErrMode::Backtrack(_)) => {
-                    self.input.reset(start);
+                    self.input.reset(&start);
                     self.state = Some(State::Done);
                     None
                 }
@@ -444,8 +463,12 @@
 /// - [`Parser::default_value`]
 /// - [`Parser::map`]
 ///
+/// <div class="warning">
+///
 /// **Note:** This never advances the [`Stream`]
 ///
+/// </div>
+///
 /// # Example
 ///
 /// ```rust
@@ -467,16 +490,15 @@
 /// ```
 #[doc(alias = "value")]
 #[doc(alias = "success")]
-pub fn empty<I: Stream, E: ParserError<I>>(_input: &mut I) -> PResult<(), E> {
+#[inline]
+pub fn empty<Input, Error>(_input: &mut Input) -> PResult<(), Error>
+where
+    Input: Stream,
+    Error: ParserError<Input>,
+{
     Ok(())
 }
 
-/// Deprecated, replaced with [`empty`] + [`Parser::value`]
-#[deprecated(since = "0.5.35", note = "Replaced with empty.value(...)`")]
-pub fn success<I: Stream, O: Clone, E: ParserError<I>>(val: O) -> impl Parser<I, O, E> {
-    trace("success", move |_input: &mut I| Ok(val.clone()))
-}
-
 /// A parser which always fails.
 ///
 /// For example, it can be used as the last alternative in `alt` to
@@ -493,8 +515,13 @@
 /// assert_eq!(fail::<_, &str, _>.parse_peek(s), Err(ErrMode::Backtrack(InputError::new(s, ErrorKind::Fail))));
 /// ```
 #[doc(alias = "unexpected")]
-pub fn fail<I: Stream, O, E: ParserError<I>>(i: &mut I) -> PResult<O, E> {
-    trace("fail", |i: &mut I| {
+#[inline]
+pub fn fail<Input, Output, Error>(i: &mut Input) -> PResult<Output, Error>
+where
+    Input: Stream,
+    Error: ParserError<Input>,
+{
+    trace("fail", |i: &mut Input| {
         Err(ErrMode::from_error_kind(i, ErrorKind::Fail))
     })
     .parse_next(i)
diff --git a/crates/winnow/src/combinator/debug/internals.rs b/crates/winnow/src/combinator/debug/internals.rs
index c38b11e..d210374 100644
--- a/crates/winnow/src/combinator/debug/internals.rs
+++ b/crates/winnow/src/combinator/debug/internals.rs
@@ -6,7 +6,7 @@
 use crate::stream::Stream;
 use crate::*;
 
-pub struct Trace<P, D, I, O, E>
+pub(crate) struct Trace<P, D, I, O, E>
 where
     P: Parser<I, O, E>,
     I: Stream,
@@ -27,7 +27,7 @@
     D: std::fmt::Display,
 {
     #[inline(always)]
-    pub fn new(parser: P, name: D) -> Self {
+    pub(crate) fn new(parser: P, name: D) -> Self {
         Self {
             parser,
             name,
@@ -62,19 +62,19 @@
     }
 }
 
-pub struct Depth {
+pub(crate) struct Depth {
     depth: usize,
     inc: bool,
 }
 
 impl Depth {
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
         let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
         let inc = true;
         Self { depth, inc }
     }
 
-    pub fn existing() -> Self {
+    pub(crate) fn existing() -> Self {
         let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst);
         let inc = false;
         Self { depth, inc }
@@ -107,7 +107,7 @@
 
 static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
 
-pub enum Severity {
+pub(crate) enum Severity {
     Success,
     Backtrack,
     Cut,
@@ -115,7 +115,7 @@
 }
 
 impl Severity {
-    pub fn with_result<T, E>(result: &Result<T, ErrMode<E>>) -> Self {
+    pub(crate) fn with_result<T, E>(result: &Result<T, ErrMode<E>>) -> Self {
         match result {
             Ok(_) => Self::Success,
             Err(ErrMode::Backtrack(_)) => Self::Backtrack,
@@ -125,7 +125,7 @@
     }
 }
 
-pub fn start<I: Stream>(
+pub(crate) fn start<I: Stream>(
     depth: usize,
     name: &dyn crate::lib::std::fmt::Display,
     count: usize,
@@ -178,7 +178,7 @@
     );
 }
 
-pub fn end(
+pub(crate) fn end(
     depth: usize,
     name: &dyn crate::lib::std::fmt::Display,
     count: usize,
@@ -199,7 +199,7 @@
     let (status_style, status) = match severity {
         Severity::Success => {
             let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into()));
-            let status = format!("+{}", consumed);
+            let status = format!("+{consumed}");
             (style, status)
         }
         Severity::Backtrack => (
@@ -228,7 +228,7 @@
     );
 }
 
-pub fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) {
+pub(crate) fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) {
     let gutter_style = anstyle::Style::new().bold();
 
     let (call_width, _) = column_widths();
diff --git a/crates/winnow/src/combinator/debug/mod.rs b/crates/winnow/src/combinator/debug/mod.rs
index ee4c293..0b5db5e 100644
--- a/crates/winnow/src/combinator/debug/mod.rs
+++ b/crates/winnow/src/combinator/debug/mod.rs
@@ -7,9 +7,6 @@
 use crate::stream::Stream;
 use crate::Parser;
 
-#[cfg(all(feature = "debug", not(feature = "std")))]
-compile_error!("`debug` requires `std`");
-
 /// Trace the execution of the parser
 ///
 /// Note that [`Parser::context`] also provides high level trace information.
@@ -67,6 +64,14 @@
     }
 }
 
+pub(crate) struct DisplayDebug<D>(pub(crate) D);
+
+impl<D: crate::lib::std::fmt::Debug> crate::lib::std::fmt::Display for DisplayDebug<D> {
+    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+        write!(f, "{:?}", self.0)
+    }
+}
+
 #[test]
 #[cfg(feature = "std")]
 #[cfg_attr(miri, ignore)]
diff --git a/crates/winnow/src/combinator/impls.rs b/crates/winnow/src/combinator/impls.rs
new file mode 100644
index 0000000..eed9c48
--- /dev/null
+++ b/crates/winnow/src/combinator/impls.rs
@@ -0,0 +1,728 @@
+//! Opaque implementations of [`Parser`]
+
+use crate::combinator::trace;
+use crate::combinator::trace_result;
+use crate::combinator::DisplayDebug;
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+use crate::error::FromRecoverableError;
+use crate::error::{AddContext, ErrMode, ErrorKind, FromExternalError, ParserError};
+use crate::lib::std::borrow::Borrow;
+use crate::lib::std::ops::Range;
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+use crate::stream::Recover;
+use crate::stream::StreamIsPartial;
+use crate::stream::{Location, Stream};
+use crate::*;
+
+/// [`Parser`] implementation for [`Parser::by_ref`]
+pub struct ByRef<'p, P> {
+    pub(crate) p: &'p mut P,
+}
+
+impl<I, O, E, P> Parser<I, O, E> for ByRef<'_, P>
+where
+    P: Parser<I, O, E>,
+{
+    #[inline(always)]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+        self.p.parse_next(i)
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::map`]
+pub struct Map<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> O2,
+{
+    pub(crate) parser: F,
+    pub(crate) map: G,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O2, E> for Map<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> O2,
+{
+    #[inline]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+        match self.parser.parse_next(i) {
+            Err(e) => Err(e),
+            Ok(o) => Ok((self.map)(o)),
+        }
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::try_map`]
+pub struct TryMap<F, G, I, O, O2, E, E2>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> Result<O2, E2>,
+    I: Stream,
+    E: FromExternalError<I, E2>,
+{
+    pub(crate) parser: F,
+    pub(crate) map: G,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+    pub(crate) e2: core::marker::PhantomData<E2>,
+}
+
+impl<F, G, I, O, O2, E, E2> Parser<I, O2, E> for TryMap<F, G, I, O, O2, E, E2>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> Result<O2, E2>,
+    I: Stream,
+    E: FromExternalError<I, E2>,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+        let start = input.checkpoint();
+        let o = self.parser.parse_next(input)?;
+        let res = (self.map)(o).map_err(|err| {
+            input.reset(&start);
+            ErrMode::from_external_error(input, ErrorKind::Verify, err)
+        });
+        trace_result("verify", &res);
+        res
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::verify_map`]
+pub struct VerifyMap<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> Option<O2>,
+    I: Stream,
+    E: ParserError<I>,
+{
+    pub(crate) parser: F,
+    pub(crate) map: G,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O2, E> for VerifyMap<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> Option<O2>,
+    I: Stream,
+    E: ParserError<I>,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+        let start = input.checkpoint();
+        let o = self.parser.parse_next(input)?;
+        let res = (self.map)(o).ok_or_else(|| {
+            input.reset(&start);
+            ErrMode::from_error_kind(input, ErrorKind::Verify)
+        });
+        trace_result("verify", &res);
+        res
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::and_then`]
+pub struct AndThen<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: Parser<O, O2, E>,
+    O: StreamIsPartial,
+    I: Stream,
+{
+    pub(crate) outer: F,
+    pub(crate) inner: G,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O2, E> for AndThen<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: Parser<O, O2, E>,
+    O: StreamIsPartial,
+    I: Stream,
+{
+    #[inline(always)]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+        let start = i.checkpoint();
+        let mut o = self.outer.parse_next(i)?;
+        let _ = o.complete();
+        let o2 = self.inner.parse_next(&mut o).map_err(|err| {
+            i.reset(&start);
+            err
+        })?;
+        Ok(o2)
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::parse_to`]
+pub struct ParseTo<P, I, O, O2, E>
+where
+    P: Parser<I, O, E>,
+    I: Stream,
+    O: crate::stream::ParseSlice<O2>,
+    E: ParserError<I>,
+{
+    pub(crate) p: P,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<P, I, O, O2, E> Parser<I, O2, E> for ParseTo<P, I, O, O2, E>
+where
+    P: Parser<I, O, E>,
+    I: Stream,
+    O: crate::stream::ParseSlice<O2>,
+    E: ParserError<I>,
+{
+    #[inline]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+        let start = i.checkpoint();
+        let o = self.p.parse_next(i)?;
+        let res = o.parse_slice().ok_or_else(|| {
+            i.reset(&start);
+            ErrMode::from_error_kind(i, ErrorKind::Verify)
+        });
+        trace_result("verify", &res);
+        res
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::flat_map`]
+pub struct FlatMap<F, G, H, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> H,
+    H: Parser<I, O2, E>,
+{
+    pub(crate) f: F,
+    pub(crate) g: G,
+    pub(crate) h: core::marker::PhantomData<H>,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, H, I, O, O2, E> Parser<I, O2, E> for FlatMap<F, G, H, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(O) -> H,
+    H: Parser<I, O2, E>,
+{
+    #[inline(always)]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+        let o = self.f.parse_next(i)?;
+        (self.g)(o).parse_next(i)
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::complete_err`]
+pub struct CompleteErr<F> {
+    pub(crate) f: F,
+}
+
+impl<F, I, O, E> Parser<I, O, E> for CompleteErr<F>
+where
+    I: Stream,
+    F: Parser<I, O, E>,
+    E: ParserError<I>,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<O, E> {
+        trace("complete_err", |input: &mut I| {
+            match (self.f).parse_next(input) {
+                Err(ErrMode::Incomplete(_)) => {
+                    Err(ErrMode::from_error_kind(input, ErrorKind::Complete))
+                }
+                rest => rest,
+            }
+        })
+        .parse_next(input)
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::verify`]
+pub struct Verify<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(&O2) -> bool,
+    I: Stream,
+    O: Borrow<O2>,
+    O2: ?Sized,
+    E: ParserError<I>,
+{
+    pub(crate) parser: F,
+    pub(crate) filter: G,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O, E> for Verify<F, G, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    G: FnMut(&O2) -> bool,
+    I: Stream,
+    O: Borrow<O2>,
+    O2: ?Sized,
+    E: ParserError<I>,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<O, E> {
+        let start = input.checkpoint();
+        let o = self.parser.parse_next(input)?;
+        let res = (self.filter)(o.borrow()).then_some(o).ok_or_else(|| {
+            input.reset(&start);
+            ErrMode::from_error_kind(input, ErrorKind::Verify)
+        });
+        trace_result("verify", &res);
+        res
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::value`]
+pub struct Value<F, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    O2: Clone,
+{
+    pub(crate) parser: F,
+    pub(crate) val: O2,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, O2, E> Parser<I, O2, E> for Value<F, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    O2: Clone,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+        (self.parser).parse_next(input).map(|_| self.val.clone())
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::default_value`]
+pub struct DefaultValue<F, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    O2: core::default::Default,
+{
+    pub(crate) parser: F,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, O2, E> Parser<I, O2, E> for DefaultValue<F, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    O2: core::default::Default,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+        (self.parser).parse_next(input).map(|_| O2::default())
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::void`]
+pub struct Void<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> Parser<I, (), E> for Void<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+{
+    #[inline(always)]
+    fn parse_next(&mut self, input: &mut I) -> PResult<(), E> {
+        (self.parser).parse_next(input).map(|_| ())
+    }
+}
+
+/// Replaced with [`Take`]
+#[deprecated(since = "0.6.14", note = "Replaced with `Take`")]
+pub type Recognize<F, I, O, E> = Take<F, I, O, E>;
+
+/// [`Parser`] implementation for [`Parser::take`]
+pub struct Take<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<I, O, E, F> Parser<I, <I as Stream>::Slice, E> for Take<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<<I as Stream>::Slice, E> {
+        let checkpoint = input.checkpoint();
+        match (self.parser).parse_next(input) {
+            Ok(_) => {
+                let offset = input.offset_from(&checkpoint);
+                input.reset(&checkpoint);
+                let taken = input.next_slice(offset);
+                Ok(taken)
+            }
+            Err(e) => Err(e),
+        }
+    }
+}
+
+/// Replaced with [`WithTaken`]
+#[deprecated(since = "0.6.14", note = "Replaced with `WithTaken`")]
+pub type WithRecognized<F, I, O, E> = WithTaken<F, I, O, E>;
+
+/// [`Parser`] implementation for [`Parser::with_taken`]
+pub struct WithTaken<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> Parser<I, (O, <I as Stream>::Slice), E> for WithTaken<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<(O, <I as Stream>::Slice), E> {
+        let checkpoint = input.checkpoint();
+        match (self.parser).parse_next(input) {
+            Ok(result) => {
+                let offset = input.offset_from(&checkpoint);
+                input.reset(&checkpoint);
+                let taken = input.next_slice(offset);
+                Ok((result, taken))
+            }
+            Err(e) => Err(e),
+        }
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::span`]
+pub struct Span<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream + Location,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<I, O, E, F> Parser<I, Range<usize>, E> for Span<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream + Location,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<Range<usize>, E> {
+        let start = input.location();
+        self.parser.parse_next(input).map(move |_| {
+            let end = input.location();
+            start..end
+        })
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::with_span`]
+pub struct WithSpan<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream + Location,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> Parser<I, (O, Range<usize>), E> for WithSpan<F, I, O, E>
+where
+    F: Parser<I, O, E>,
+    I: Stream + Location,
+{
+    #[inline]
+    fn parse_next(&mut self, input: &mut I) -> PResult<(O, Range<usize>), E> {
+        let start = input.location();
+        self.parser.parse_next(input).map(move |output| {
+            let end = input.location();
+            (output, (start..end))
+        })
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::output_into`]
+pub struct OutputInto<F, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    O: Into<O2>,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) o2: core::marker::PhantomData<O2>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, O2, E> Parser<I, O2, E> for OutputInto<F, I, O, O2, E>
+where
+    F: Parser<I, O, E>,
+    O: Into<O2>,
+{
+    #[inline]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+        self.parser.parse_next(i).map(|o| o.into())
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::err_into`]
+pub struct ErrInto<F, I, O, E, E2>
+where
+    F: Parser<I, O, E>,
+    E: Into<E2>,
+{
+    pub(crate) parser: F,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+    pub(crate) e2: core::marker::PhantomData<E2>,
+}
+
+impl<F, I, O, E, E2> Parser<I, O, E2> for ErrInto<F, I, O, E, E2>
+where
+    F: Parser<I, O, E>,
+    E: Into<E2>,
+{
+    #[inline]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O, E2> {
+        self.parser
+            .parse_next(i)
+            .map_err(|err| err.map(|e| e.into()))
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::context`]
+pub struct Context<F, I, O, E, C>
+where
+    F: Parser<I, O, E>,
+    I: Stream,
+    E: AddContext<I, C>,
+    C: Clone + crate::lib::std::fmt::Debug,
+{
+    pub(crate) parser: F,
+    pub(crate) context: C,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E, C> Parser<I, O, E> for Context<F, I, O, E, C>
+where
+    F: Parser<I, O, E>,
+    I: Stream,
+    E: AddContext<I, C>,
+    C: Clone + crate::lib::std::fmt::Debug,
+{
+    #[inline]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+        let context = self.context.clone();
+        trace(DisplayDebug(self.context.clone()), move |i: &mut I| {
+            let start = i.checkpoint();
+            (self.parser)
+                .parse_next(i)
+                .map_err(|err| err.add_context(i, &start, context.clone()))
+        })
+        .parse_next(i)
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::retry_after`]
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+pub struct RetryAfter<P, R, I, O, E>
+where
+    P: Parser<I, O, E>,
+    R: Parser<I, (), E>,
+    I: Stream,
+    I: Recover<E>,
+    E: FromRecoverableError<I, E>,
+{
+    pub(crate) parser: P,
+    pub(crate) recover: R,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+impl<P, R, I, O, E> Parser<I, O, E> for RetryAfter<P, R, I, O, E>
+where
+    P: Parser<I, O, E>,
+    R: Parser<I, (), E>,
+    I: Stream,
+    I: Recover<E>,
+    E: FromRecoverableError<I, E>,
+{
+    #[inline(always)]
+    fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+        if I::is_recovery_supported() {
+            retry_after_inner(&mut self.parser, &mut self.recover, i)
+        } else {
+            self.parser.parse_next(i)
+        }
+    }
+}
+
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+fn retry_after_inner<P, R, I, O, E>(parser: &mut P, recover: &mut R, i: &mut I) -> PResult<O, E>
+where
+    P: Parser<I, O, E>,
+    R: Parser<I, (), E>,
+    I: Stream,
+    I: Recover<E>,
+    E: FromRecoverableError<I, E>,
+{
+    loop {
+        let token_start = i.checkpoint();
+        let mut err = match parser.parse_next(i) {
+            Ok(o) => {
+                return Ok(o);
+            }
+            Err(ErrMode::Incomplete(e)) => return Err(ErrMode::Incomplete(e)),
+            Err(err) => err,
+        };
+        let err_start = i.checkpoint();
+        let err_start_eof_offset = i.eof_offset();
+        if recover.parse_next(i).is_ok() {
+            let i_eof_offset = i.eof_offset();
+            if err_start_eof_offset == i_eof_offset {
+                // Didn't advance so bubble the error up
+            } else if let Err(err_) = i.record_err(&token_start, &err_start, err) {
+                err = err_;
+            } else {
+                continue;
+            }
+        }
+
+        i.reset(&err_start);
+        err = err.map(|err| E::from_recoverable_error(&token_start, &err_start, i, err));
+        return Err(err);
+    }
+}
+
+/// [`Parser`] implementation for [`Parser::resume_after`]
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+pub struct ResumeAfter<P, R, I, O, E>
+where
+    P: Parser<I, O, E>,
+    R: Parser<I, (), E>,
+    I: Stream,
+    I: Recover<E>,
+    E: FromRecoverableError<I, E>,
+{
+    pub(crate) parser: P,
+    pub(crate) recover: R,
+    pub(crate) i: core::marker::PhantomData<I>,
+    pub(crate) o: core::marker::PhantomData<O>,
+    pub(crate) e: core::marker::PhantomData<E>,
+}
+
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+impl<P, R, I, O, E> Parser<I, Option<O>, E> for ResumeAfter<P, R, I, O, E>
+where
+    P: Parser<I, O, E>,
+    R: Parser<I, (), E>,
+    I: Stream,
+    I: Recover<E>,
+    E: FromRecoverableError<I, E>,
+{
+    #[inline(always)]
+    fn parse_next(&mut self, i: &mut I) -> PResult<Option<O>, E> {
+        if I::is_recovery_supported() {
+            resume_after_inner(&mut self.parser, &mut self.recover, i)
+        } else {
+            self.parser.parse_next(i).map(Some)
+        }
+    }
+}
+
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+fn resume_after_inner<P, R, I, O, E>(
+    parser: &mut P,
+    recover: &mut R,
+    i: &mut I,
+) -> PResult<Option<O>, E>
+where
+    P: Parser<I, O, E>,
+    R: Parser<I, (), E>,
+    I: Stream,
+    I: Recover<E>,
+    E: FromRecoverableError<I, E>,
+{
+    let token_start = i.checkpoint();
+    let mut err = match parser.parse_next(i) {
+        Ok(o) => {
+            return Ok(Some(o));
+        }
+        Err(ErrMode::Incomplete(e)) => return Err(ErrMode::Incomplete(e)),
+        Err(err) => err,
+    };
+    let err_start = i.checkpoint();
+    if recover.parse_next(i).is_ok() {
+        if let Err(err_) = i.record_err(&token_start, &err_start, err) {
+            err = err_;
+        } else {
+            return Ok(None);
+        }
+    }
+
+    i.reset(&err_start);
+    err = err.map(|err| E::from_recoverable_error(&token_start, &err_start, i, err));
+    Err(err)
+}
diff --git a/crates/winnow/src/combinator/mod.rs b/crates/winnow/src/combinator/mod.rs
index da5fa79..a7f540c 100644
--- a/crates/winnow/src/combinator/mod.rs
+++ b/crates/winnow/src/combinator/mod.rs
@@ -1,20 +1,24 @@
 //! # List of parsers and combinators
 //!
+//! <div class="warning">
+//!
 //! **Note**: this list is meant to provide a nicer way to find a parser than reading through the documentation on docs.rs. Function combinators are organized in module so they are a bit easier to find.
 //!
+//! </div>
+//!
 //! ## Basic elements
 //!
-//! Those are used to recognize the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer".
+//! Those are used to take a series of tokens for the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer".
 //!
 //! | combinator | usage | input | new input | output | comment |
 //! |---|---|---|---|---|---|
-//! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` |  `"abc"` |  `"bc"` | `Ok('a')` |Matches one of the provided characters (works with non ASCII characters too)|
-//! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` |  `"xyab"` |  `"yab"` | `Ok('x')` |Matches anything but the provided characters|
-//! | [`tag`][crate::token::tag] | `"hello"` |  `"hello world"` |  `" world"` | `Ok("hello")` |Recognizes a specific suite of characters or bytes (see also [`Caseless`][crate::ascii::Caseless])|
+//! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` |  `"abc"` |  `"bc"` | `Ok('a')` |Matches one of the provided [set of tokens][crate::stream::ContainsToken] (works with non ASCII characters too)|
+//! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` |  `"xyab"` |  `"yab"` | `Ok('x')` |Matches anything but one of the provided [set of tokens][crate::stream::ContainsToken]|
+//! | [`literal`][crate::token::literal] | `"hello"` |  `"hello world"` |  `" world"` | `Ok("hello")` |Recognizes a specific suite of characters or bytes (see also [`Caseless`][crate::ascii::Caseless])|
 //! | [`take`][crate::token::take] | `take(4)` |  `"hello"` |  `"o"` | `Ok("hell")` |Takes a specific number of bytes or characters|
-//! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` |  `"abc123"` |  `"123"` | `Ok("abc")` |Returns the longest list of bytes for which the provided pattern matches.|
-//! | [`take_till0`][crate::token::take_till0] | `take_till0(is_alphabetic)` |  `"123abc"` |  `"abc"` | `Ok("123")` |Returns the longest list of bytes or characters until the provided pattern matches. `take_till1` does the same, but must return at least one character. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`|
-//! | [`take_until`][crate::token::take_until] | `take_until(0.., "world")` |  `"Hello world"` |  `"world"` | `Ok("Hello ")` |Returns the longest list of bytes or characters until the provided tag is found.|
+//! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` |  `"abc123"` |  `"123"` | `Ok("abc")` |Returns the longest slice of bytes or characters for which the provided [set of tokens][crate::stream::ContainsToken] matches.|
+//! | [`take_till`][crate::token::take_till] | `take_till(0.., is_alphabetic)` |  `"123abc"` |  `"abc"` | `Ok("123")` |Returns a slice of bytes or characters until the provided [set of tokens][crate::stream::ContainsToken] matches. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`|
+//! | [`take_until`][crate::token::take_until] | `take_until(0.., "world")` |  `"Hello world"` |  `"world"` | `Ok("Hello ")` |Returns a slice of bytes or characters until the provided [literal][crate::token::literal] is found.|
 //!
 //! ## Choice combinators
 //!
@@ -28,21 +32,21 @@
 //!
 //! | combinator | usage | input | new input | output | comment |
 //! |---|---|---|---|---|---|
-//! | [`(...)` (tuples)][crate::Parser] | `("ab", "XY", take(1))` | `"abXYZ!"` | `"!"` | `Ok(("ab", "XY", "Z"))` |Chains parsers and assemble the sub results in a tuple. You can use as many child parsers as you can put elements in a tuple|
-//! | [`seq!`] | `seq!(_: char('('), take(2), _: char(')'))` | `"(ab)cd"` | `"cd"` | `Ok("ab")` ||
-//! | [`delimited`] | `delimited(char('('), take(2), char(')'))` | `"(ab)cd"` | `"cd"` | `Ok("ab")` ||
-//! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("XY")` ||
-//! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("ab")` ||
-//! | [`separated_pair`] | `separated_pair("hello", char(','), "world")` | `"hello,world!"` | `"!"` | `Ok(("hello", "world"))` ||
+//! | [`(...)` (tuples)][crate::Parser] | `("ab", "XY", take(1))` | `"abXYZ!"` | `"!"` | `Ok(("ab", "XY", "Z"))` |Parse a series of values|
+//! | [`seq!`] | `seq!(_: '(', take(2), _: ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse a series of values, discarding those you specify|
+//! | [`delimited`] | `delimited('(', take(2), ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse three values, discarding the first and third value|
+//! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("XY")` |Parse two values, discarding the first value|
+//! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("ab")` |Parse two values, discarding the second value|
+//! | [`separated_pair`] | `separated_pair("hello", ',', "world")` | `"hello,world!"` | `"!"` | `Ok(("hello", "world"))` | Parse three values, discarding the middle value|
 //!
 //! ## Applying a parser multiple times
 //!
 //! | combinator | usage | input | new input | output | comment |
 //! |---|---|---|---|---|---|
 //! | [`repeat`] | `repeat(1..=3, "ab")` | `"ababc"` | `"c"` | `Ok(vec!["ab", "ab"])` |Applies the parser between m and n times (n included) and returns the list of results in a Vec|
-//! | [`repeat_till`] | `repeat_till(0.., tag( "ab" ), tag( "ef" ))` | `"ababefg"` | `"g"` | `Ok((vec!["ab", "ab"], "ef"))` |Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second|
+//! | [`repeat_till`] | `repeat_till(0.., "ab", "ef")` | `"ababefg"` | `"g"` | `Ok((vec!["ab", "ab"], "ef"))` |Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second|
 //! | [`separated`] | `separated(1..=3, "ab", ",")` | `"ab,ab,ab."` | `"."` | `Ok(vec!["ab", "ab", "ab"])` |Applies the parser and separator between m and n times (n included) and returns the list of results in a Vec|
-//! | [`fold_repeat`] | `fold_repeat(1..=2, be_u8, \|\| 0, \|acc, item\| acc + item)` | `[1, 2, 3]` | `[3]` | `Ok(3)` |Applies the parser between m and n times (n included) and folds the list of return value|
+//! | [`Repeat::fold`] | <code>repeat(1..=2, `be_u8`).fold(\|\| 0, \|acc, item\| acc + item)</code> | `[1, 2, 3]` | `[3]` | `Ok(3)` |Applies the parser between m and n times (n included) and folds the list of return value|
 //!
 //! ## Partial related
 //!
@@ -64,8 +68,8 @@
 //! - [`not`]: Returns a result only if the embedded parser returns `Backtrack` or `Incomplete`. Does not consume the input
 //! - [`opt`]: Make the underlying parser optional
 //! - [`peek`]: Returns a result without consuming the input
-//! - [`Parser::recognize`]: If the child parser was successful, return the consumed input as the produced value
-//! - [`Parser::with_recognized`]: If the child parser was successful, return a tuple of the consumed input and the produced output.
+//! - [`Parser::take`]: If the child parser was successful, return the consumed input as the produced value
+//! - [`Parser::with_taken`]: If the child parser was successful, return a tuple of the consumed input and the produced output.
 //! - [`Parser::span`]: If the child parser was successful, return the location of the consumed input as the produced value
 //! - [`Parser::with_span`]: If the child parser was successful, return a tuple of the location of the consumed input and the produced output.
 //! - [`Parser::verify`]: Returns the result of the child parser if it satisfies a verification function
@@ -81,8 +85,8 @@
 //!
 //! ## Remaining combinators
 //!
-//! - [`success`]: Returns a value without consuming any input, always succeeds
-//! - [`fail`]: Inversion of `success`. Always fails.
+//! - [`empty`]: Returns a value without consuming any input, always succeeds
+//! - [`fail`]: Inversion of [`empty`]. Always fails.
 //! - [`Parser::by_ref`]: Allow moving `&mut impl Parser` into other parsers
 //!
 //! ## Text parsing
@@ -108,8 +112,8 @@
 //! - [`dec_uint`][crate::ascii::dec_uint]: Decode a variable-width, decimal unsigned integer
 //! - [`hex_uint`][crate::ascii::hex_uint]: Decode a variable-width, hexadecimal integer
 //!
-//! - [`escaped`][crate::ascii::escaped]: Matches a byte string with escaped characters
-//! - [`escaped_transform`][crate::ascii::escaped_transform]: Matches a byte string with escaped characters, and returns a new string with the escaped characters replaced
+//! - [`take_escaped`][crate::ascii::take_escaped]: Recognize the input slice with escaped characters
+//! - [`escaped_transform`][crate::ascii::escaped_transform]: Parse escaped characters, unescaping them
 //!
 //! ### Character test functions
 //!
@@ -125,9 +129,9 @@
 //!
 //! ## Binary format parsing
 //!
-//! - [`length_count`][crate::binary::length_count] Gets a number from the first parser, then applies the second parser that many times
-//! - [`length_data`][crate::binary::length_data]: Gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice
-//! - [`length_value`][crate::binary::length_value]: Gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns `Incomplete`, `length_value` will return an error
+//! - [`length_repeat`][crate::binary::length_repeat] Gets a number from the first parser, then applies the second parser that many times
+//! - [`length_take`][crate::binary::length_take]: Gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice
+//! - [`length_and_then`][crate::binary::length_and_then]: Gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns `Incomplete`, `length_value` will return an error
 //!
 //! ### Integers
 //!
@@ -152,24 +156,26 @@
 //! - [`bits`][crate::binary::bits::bits]: Transforms the current input type (byte slice `&[u8]`) to a bit stream on which bit specific parsers and more general combinators can be applied
 //! - [`bytes`][crate::binary::bits::bytes]: Transforms its bits stream input back into a byte slice for the underlying parser
 //! - [`take`][crate::binary::bits::take]: Take a set number of bits
-//! - [`tag`][crate::binary::bits::tag]: Check if a set number of bits matches a pattern
+//! - [`pattern`][crate::binary::bits::pattern]: Check if a set number of bits matches a pattern
 //! - [`bool`][crate::binary::bits::bool]: Match any one bit
 
 mod branch;
 mod core;
 mod debug;
 mod multi;
-mod parser;
 mod sequence;
 
 #[cfg(test)]
 mod tests;
 
+pub mod impls;
+
 pub use self::branch::*;
 pub use self::core::*;
 pub use self::debug::*;
+#[deprecated(since = "0.6.23", note = "Replaced with `combinator::impls`")]
+pub use self::impls::*;
 pub use self::multi::*;
-pub use self::parser::*;
 pub use self::sequence::*;
 
 #[allow(unused_imports)]
diff --git a/crates/winnow/src/combinator/multi.rs b/crates/winnow/src/combinator/multi.rs
index f76d635..38558f3 100644
--- a/crates/winnow/src/combinator/multi.rs
+++ b/crates/winnow/src/combinator/multi.rs
@@ -3,6 +3,7 @@
 use crate::combinator::trace;
 use crate::error::ErrMode;
 use crate::error::ErrorKind;
+use crate::error::FromExternalError;
 use crate::error::ParserError;
 use crate::stream::Accumulate;
 use crate::stream::Range;
@@ -15,17 +16,18 @@
 /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
 /// [`cut_err`][crate::combinator::cut_err].
 ///
-/// # Arguments
-/// * `m` The minimum number of iterations.
-/// * `n` The maximum number of iterations.
-/// * `f` The parser to apply.
+/// To take a series of tokens, [`Accumulate`] into a `()`
+/// (e.g. with [`.map(|()| ())`][Parser::map])
+/// and then [`Parser::take`].
 ///
-/// To recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`].
+/// <div class="warning">
 ///
 /// **Warning:** If the parser passed to `repeat` accepts empty inputs
 /// (like `alpha0` or `digit0`), `repeat` will return an error,
 /// to prevent going into an infinite loop.
 ///
+/// </div>
+///
 /// # Example
 ///
 /// Zero or more repetitions:
@@ -34,7 +36,6 @@
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::repeat;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   repeat(0.., "abc").parse_peek(s)
@@ -53,7 +54,6 @@
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::repeat;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   repeat(1.., "abc").parse_peek(s)
@@ -72,7 +72,6 @@
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::repeat;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   repeat(2, "abc").parse_peek(s)
@@ -92,7 +91,6 @@
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::repeat;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   repeat(0..=2, "abc").parse_peek(s)
@@ -115,15 +113,18 @@
 #[doc(alias = "skip_many")]
 #[doc(alias = "skip_many1")]
 #[inline(always)]
-pub fn repeat<I, O, C, E, P>(range: impl Into<Range>, parser: P) -> Repeat<P, I, O, C, E>
+pub fn repeat<Input, Output, Accumulator, Error, ParseNext>(
+    occurrences: impl Into<Range>,
+    parser: ParseNext,
+) -> Repeat<ParseNext, Input, Output, Accumulator, Error>
 where
-    I: Stream,
-    C: Accumulate<O>,
-    P: Parser<I, O, E>,
-    E: ParserError<I>,
+    Input: Stream,
+    Accumulator: Accumulate<Output>,
+    ParseNext: Parser<Input, Output, Error>,
+    Error: ParserError<Input>,
 {
     Repeat {
-        range: range.into(),
+        occurrences: occurrences.into(),
         parser,
         i: Default::default(),
         o: Default::default(),
@@ -132,8 +133,7 @@
     }
 }
 
-/// Implementation of [`repeat`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+/// Customizable [`Parser`] implementation for [`repeat`]
 pub struct Repeat<P, I, O, C, E>
 where
     P: Parser<I, O, E>,
@@ -141,7 +141,7 @@
     C: Accumulate<O>,
     E: ParserError<I>,
 {
-    range: Range,
+    occurrences: Range,
     parser: P,
     i: core::marker::PhantomData<I>,
     o: core::marker::PhantomData<O>,
@@ -149,26 +149,30 @@
     e: core::marker::PhantomData<E>,
 }
 
-impl<P, I, O, E> Repeat<P, I, O, (), E>
+impl<ParseNext, Input, Output, Error> Repeat<ParseNext, Input, Output, (), Error>
 where
-    P: Parser<I, O, E>,
-    I: Stream,
-    E: ParserError<I>,
+    ParseNext: Parser<Input, Output, Error>,
+    Input: Stream,
+    Error: ParserError<Input>,
 {
-    /// Repeats the embedded parser, calling `g` to gather the results
+    /// Repeats the embedded parser, calling `op` to gather the results
     ///
     /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
     /// [`cut_err`][crate::combinator::cut_err].
     ///
     /// # Arguments
     /// * `init` A function returning the initial value.
-    /// * `g` The function that combines a result of `f` with
+    /// * `op` The function that combines a result of `f` with
     ///       the current accumulator.
     ///
+    /// <div class="warning">
+    ///
     /// **Warning:** If the parser passed to `fold` accepts empty inputs
     /// (like `alpha0` or `digit0`), `fold_repeat` will return an error,
     /// to prevent going into an infinite loop.
     ///
+    /// </div>
+    ///
     /// # Example
     ///
     /// Zero or more repetitions:
@@ -176,7 +180,6 @@
     /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
     /// # use winnow::prelude::*;
     /// use winnow::combinator::repeat;
-    /// use winnow::token::tag;
     ///
     /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
     ///   repeat(
@@ -202,7 +205,6 @@
     /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
     /// # use winnow::prelude::*;
     /// use winnow::combinator::repeat;
-    /// use winnow::token::tag;
     ///
     /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
     ///   repeat(
@@ -228,7 +230,6 @@
     /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
     /// # use winnow::prelude::*;
     /// use winnow::combinator::repeat;
-    /// use winnow::token::tag;
     ///
     /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
     ///   repeat(
@@ -254,30 +255,185 @@
     #[doc(alias = "fold_many_m_n")]
     #[doc(alias = "fold_repeat")]
     #[inline(always)]
-    pub fn fold<H, G, R>(mut self, mut init: H, mut g: G) -> impl Parser<I, R, E>
+    pub fn fold<Init, Op, Result>(
+        mut self,
+        mut init: Init,
+        mut op: Op,
+    ) -> impl Parser<Input, Result, Error>
     where
-        G: FnMut(R, O) -> R,
-        H: FnMut() -> R,
+        Init: FnMut() -> Result,
+        Op: FnMut(Result, Output) -> Result,
     {
         let Range {
             start_inclusive,
             end_inclusive,
-        } = self.range;
-        trace("repeat_fold", move |i: &mut I| {
+        } = self.occurrences;
+        trace("repeat_fold", move |i: &mut Input| {
             match (start_inclusive, end_inclusive) {
-                (0, None) => fold_repeat0_(&mut self.parser, &mut init, &mut g, i),
-                (1, None) => fold_repeat1_(&mut self.parser, &mut init, &mut g, i),
+                (0, None) => fold_repeat0_(&mut self.parser, &mut init, &mut op, i),
+                (1, None) => fold_repeat1_(&mut self.parser, &mut init, &mut op, i),
                 (start, end) => fold_repeat_m_n_(
                     start,
                     end.unwrap_or(usize::MAX),
                     &mut self.parser,
                     &mut init,
-                    &mut g,
+                    &mut op,
                     i,
                 ),
             }
         })
     }
+
+    /// Akin to [`Repeat::fold`], but for containers that can reject an element.
+    ///
+    /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
+    /// [`cut_err`][crate::combinator::cut_err]. Additionally, if the fold function returns `None`, the parser will
+    /// stop and return an error.
+    ///
+    /// # Arguments
+    /// * `init` A function returning the initial value.
+    /// * `op` The function that combines a result of `f` with
+    ///       the current accumulator.
+    ///
+    /// <div class="warning">
+    ///
+    /// **Warning:** If the parser passed to `repeat` accepts empty inputs
+    /// (like `alpha0` or `digit0`), `verify_fold` will return an error,
+    /// to prevent going into an infinite loop.
+    ///
+    /// </div>
+    ///
+    /// # Example
+    ///
+    /// Guaranteeing that the input had unique elements:
+    /// ```rust
+    /// # use winnow::error::IResult;
+    /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+    /// # use winnow::prelude::*;
+    /// use winnow::combinator::repeat;
+    /// use std::collections::HashSet;
+    ///
+    /// fn parser(s: &str) -> IResult<&str, HashSet<&str>> {
+    ///   repeat(
+    ///     0..,
+    ///     "abc"
+    ///   ).verify_fold(
+    ///     HashSet::new,
+    ///     |mut acc: HashSet<_>, item| {
+    ///       if acc.insert(item) {
+    ///          Some(acc)
+    ///       } else {
+    ///          None
+    ///       }
+    ///     }
+    ///   ).parse_peek(s)
+    /// }
+    ///
+    /// assert_eq!(parser("abc"), Ok(("", HashSet::from(["abc"]))));
+    /// assert_eq!(parser("abcabc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Verify))));
+    /// assert_eq!(parser("abc123"), Ok(("123", HashSet::from(["abc"]))));
+    /// assert_eq!(parser("123123"), Ok(("123123", HashSet::from([]))));
+    /// assert_eq!(parser(""), Ok(("", HashSet::from([]))));
+    /// ```
+    #[inline(always)]
+    pub fn verify_fold<Init, Op, Result>(
+        mut self,
+        mut init: Init,
+        mut op: Op,
+    ) -> impl Parser<Input, Result, Error>
+    where
+        Init: FnMut() -> Result,
+        Op: FnMut(Result, Output) -> Option<Result>,
+    {
+        let Range {
+            start_inclusive,
+            end_inclusive,
+        } = self.occurrences;
+        trace("repeat_verify_fold", move |input: &mut Input| {
+            verify_fold_m_n(
+                start_inclusive,
+                end_inclusive.unwrap_or(usize::MAX),
+                &mut self.parser,
+                &mut init,
+                &mut op,
+                input,
+            )
+        })
+    }
+
+    /// Akin to [`Repeat::fold`], but for containers that can error when an element is accumulated.
+    ///
+    /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
+    /// [`cut_err`][crate::combinator::cut_err]. Additionally, if the fold function returns an error, the parser will
+    /// stop and return it.
+    ///
+    /// # Arguments
+    /// * `init` A function returning the initial value.
+    /// * `op` The function that combines a result of `f` with
+    ///       the current accumulator.
+    ///
+    /// <div class="warning">
+    ///
+    /// **Warning:** If the parser passed to `repeat` accepts empty inputs
+    /// (like `alpha0` or `digit0`), `try_fold` will return an error,
+    /// to prevent going into an infinite loop.
+    ///
+    /// </div>
+    ///
+    /// # Example
+    ///
+    /// Writing the output to a vector of bytes:
+    /// ```rust
+    /// # use winnow::error::IResult;
+    /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+    /// # use winnow::prelude::*;
+    /// use winnow::combinator::repeat;
+    /// use std::io::Write;
+    /// use std::io::Error;
+    ///
+    /// fn parser(s: &str) -> IResult<&str, Vec<u8>> {
+    ///   repeat(
+    ///     0..,
+    ///     "abc"
+    ///   ).try_fold(
+    ///     Vec::new,
+    ///     |mut acc, item: &str| -> Result<_, Error> {
+    ///       acc.write(item.as_bytes())?;
+    ///       Ok(acc)
+    ///     }
+    ///   ).parse_peek(s)
+    /// }
+    ///
+    /// assert_eq!(parser("abc"), Ok(("", b"abc".to_vec())));
+    /// assert_eq!(parser("abc123"), Ok(("123", b"abc".to_vec())));
+    /// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+    /// assert_eq!(parser(""), Ok(("", vec![])));
+    #[inline(always)]
+    pub fn try_fold<Init, Op, OpError, Result>(
+        mut self,
+        mut init: Init,
+        mut op: Op,
+    ) -> impl Parser<Input, Result, Error>
+    where
+        Init: FnMut() -> Result,
+        Op: FnMut(Result, Output) -> core::result::Result<Result, OpError>,
+        Error: FromExternalError<Input, OpError>,
+    {
+        let Range {
+            start_inclusive,
+            end_inclusive,
+        } = self.occurrences;
+        trace("repeat_try_fold", move |input: &mut Input| {
+            try_fold_m_n(
+                start_inclusive,
+                end_inclusive.unwrap_or(usize::MAX),
+                &mut self.parser,
+                &mut init,
+                &mut op,
+                input,
+            )
+        })
+    }
 }
 
 impl<P, I, O, C, E> Parser<I, C, E> for Repeat<P, I, O, C, E>
@@ -292,7 +448,7 @@
         let Range {
             start_inclusive,
             end_inclusive,
-        } = self.range;
+        } = self.occurrences;
         trace("repeat", move |i: &mut I| {
             match (start_inclusive, end_inclusive) {
                 (0, None) => repeat0_(&mut self.parser, i),
@@ -318,7 +474,7 @@
         let len = i.eof_offset();
         match f.parse_next(i) {
             Err(ErrMode::Backtrack(_)) => {
-                i.reset(start);
+                i.reset(&start);
                 return Ok(acc);
             }
             Err(e) => return Err(e),
@@ -341,8 +497,9 @@
     F: Parser<I, O, E>,
     E: ParserError<I>,
 {
+    let start = i.checkpoint();
     match f.parse_next(i) {
-        Err(e) => Err(e.append(i, ErrorKind::Many)),
+        Err(e) => Err(e.append(i, &start, ErrorKind::Many)),
         Ok(o) => {
             let mut acc = C::initial(None);
             acc.accumulate(o);
@@ -352,7 +509,7 @@
                 let len = i.eof_offset();
                 match f.parse_next(i) {
                     Err(ErrMode::Backtrack(_)) => {
-                        i.reset(start);
+                        i.reset(&start);
                         return Ok(acc);
                     }
                     Err(e) => return Err(e),
@@ -380,12 +537,19 @@
     let mut res = C::initial(Some(count));
 
     for _ in 0..count {
+        let start = i.checkpoint();
+        let len = i.eof_offset();
         match f.parse_next(i) {
             Ok(o) => {
+                // infinite loop check: the parser must always consume
+                if i.eof_offset() == len {
+                    return Err(ErrMode::assert(i, "`repeat` parsers must always consume"));
+                }
+
                 res.accumulate(o);
             }
             Err(e) => {
-                return Err(e.append(i, ErrorKind::Many));
+                return Err(e.append(i, &start, ErrorKind::Many));
             }
         }
     }
@@ -401,7 +565,10 @@
     E: ParserError<I>,
 {
     if min > max {
-        return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many)));
+        return Err(ErrMode::assert(
+            input,
+            "range should be ascending, rather than descending",
+        ));
     }
 
     let mut res = C::initial(Some(min));
@@ -422,9 +589,9 @@
             }
             Err(ErrMode::Backtrack(e)) => {
                 if count < min {
-                    return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many)));
+                    return Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Many)));
                 } else {
-                    input.reset(start);
+                    input.reset(&start);
                     return Ok(res);
                 }
             }
@@ -444,7 +611,13 @@
 ///
 /// `f` keeps going so long as `g` produces [`ErrMode::Backtrack`]. To instead chain an error up, see [`cut_err`][crate::combinator::cut_err].
 ///
-/// To recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`].
+/// To take a series of tokens, [`Accumulate`] into a `()`
+/// (e.g. with [`.map(|((), _)| ())`][Parser::map])
+/// and then [`Parser::take`].
+///
+/// See also
+/// - [`take_till`][crate::token::take_till] for recognizing up-to a member of a [set of tokens][crate::stream::ContainsToken]
+/// - [`take_until`][crate::token::take_until] for recognizing up-to a [`literal`][crate::token::literal] (w/ optional simd optimizations)
 ///
 /// # Example
 ///
@@ -453,7 +626,6 @@
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::repeat_till;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, (Vec<&str>, &str)> {
 ///   repeat_till(0.., "abc", "end").parse_peek(s)
@@ -467,44 +639,36 @@
 /// # }
 /// ```
 #[doc(alias = "many_till0")]
-pub fn repeat_till<I, O, C, P, E, F, G>(
-    range: impl Into<Range>,
-    mut f: F,
-    mut g: G,
-) -> impl Parser<I, (C, P), E>
+pub fn repeat_till<Input, Output, Accumulator, Terminator, Error, ParseNext, TerminatorParser>(
+    occurrences: impl Into<Range>,
+    mut parse: ParseNext,
+    mut terminator: TerminatorParser,
+) -> impl Parser<Input, (Accumulator, Terminator), Error>
 where
-    I: Stream,
-    C: Accumulate<O>,
-    F: Parser<I, O, E>,
-    G: Parser<I, P, E>,
-    E: ParserError<I>,
+    Input: Stream,
+    Accumulator: Accumulate<Output>,
+    ParseNext: Parser<Input, Output, Error>,
+    TerminatorParser: Parser<Input, Terminator, Error>,
+    Error: ParserError<Input>,
 {
     let Range {
         start_inclusive,
         end_inclusive,
-    } = range.into();
-    trace("repeat_till", move |i: &mut I| {
+    } = occurrences.into();
+    trace("repeat_till", move |i: &mut Input| {
         match (start_inclusive, end_inclusive) {
-            (0, None) => repeat_till0_(&mut f, &mut g, i),
-            (start, end) => repeat_till_m_n_(start, end.unwrap_or(usize::MAX), &mut f, &mut g, i),
+            (0, None) => repeat_till0_(&mut parse, &mut terminator, i),
+            (start, end) => repeat_till_m_n_(
+                start,
+                end.unwrap_or(usize::MAX),
+                &mut parse,
+                &mut terminator,
+                i,
+            ),
         }
     })
 }
 
-/// Deprecated, replaced with [`repeat_till`]
-#[deprecated(since = "0.5.35", note = "Replaced with `repeat_till`")]
-#[inline(always)]
-pub fn repeat_till0<I, O, C, P, E, F, G>(f: F, g: G) -> impl Parser<I, (C, P), E>
-where
-    I: Stream,
-    C: Accumulate<O>,
-    F: Parser<I, O, E>,
-    G: Parser<I, P, E>,
-    E: ParserError<I>,
-{
-    repeat_till(0.., f, g)
-}
-
 fn repeat_till0_<I, O, C, P, E, F, G>(f: &mut F, g: &mut G, i: &mut I) -> PResult<(C, P), E>
 where
     I: Stream,
@@ -520,9 +684,9 @@
         match g.parse_next(i) {
             Ok(o) => return Ok((res, o)),
             Err(ErrMode::Backtrack(_)) => {
-                i.reset(start);
+                i.reset(&start);
                 match f.parse_next(i) {
-                    Err(e) => return Err(e.append(i, ErrorKind::Many)),
+                    Err(e) => return Err(e.append(i, &start, ErrorKind::Many)),
                     Ok(o) => {
                         // infinite loop check: the parser must always consume
                         if i.eof_offset() == len {
@@ -553,17 +717,22 @@
     E: ParserError<I>,
 {
     if min > max {
-        return Err(ErrMode::Cut(E::from_error_kind(i, ErrorKind::Many)));
+        return Err(ErrMode::assert(
+            i,
+            "range should be ascending, rather than descending",
+        ));
     }
 
     let mut res = C::initial(Some(min));
+
+    let start = i.checkpoint();
     for _ in 0..min {
         match f.parse_next(i) {
             Ok(o) => {
                 res.accumulate(o);
             }
             Err(e) => {
-                return Err(e.append(i, ErrorKind::Many));
+                return Err(e.append(i, &start, ErrorKind::Many));
             }
         }
     }
@@ -576,10 +745,10 @@
                 if count == max {
                     return Err(ErrMode::Backtrack(err));
                 }
-                i.reset(start);
+                i.reset(&start);
                 match f.parse_next(i) {
                     Err(e) => {
-                        return Err(e.append(i, ErrorKind::Many));
+                        return Err(e.append(i, &start, ErrorKind::Many));
                     }
                     Ok(o) => {
                         // infinite loop check: the parser must always consume
@@ -602,15 +771,18 @@
 /// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
 /// [`cut_err`][crate::combinator::cut_err].
 ///
-/// # Arguments
-/// * `range` The minimum and maximum number of iterations.
-/// * `parser` The parser that parses the elements of the list.
-/// * `sep` The parser that parses the separator between list elements.
+/// To take a series of tokens, [`Accumulate`] into a `()`
+/// (e.g. with [`.map(|()| ())`][Parser::map])
+/// and then [`Parser::take`].
+///
+/// <div class="warning">
 ///
 /// **Warning:** If the separator parser accepts empty inputs
 /// (like `alpha0` or `digit0`), `separated` will return an error,
 /// to prevent going into an infinite loop.
 ///
+/// </div>
+///
 /// # Example
 ///
 /// Zero or more repetitions:
@@ -619,7 +791,6 @@
 /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::separated;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   separated(0.., "abc", "|").parse_peek(s)
@@ -639,7 +810,6 @@
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::separated;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   separated(1.., "abc", "|").parse_peek(s)
@@ -659,7 +829,6 @@
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::separated;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   separated(2, "abc", "|").parse_peek(s)
@@ -679,7 +848,6 @@
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::separated;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
 ///   separated(0..=2, "abc", "|").parse_peek(s)
@@ -698,23 +866,23 @@
 #[doc(alias = "separated_list1")]
 #[doc(alias = "separated_m_n")]
 #[inline(always)]
-pub fn separated<I, O, C, O2, E, P, S>(
-    range: impl Into<Range>,
-    mut parser: P,
-    mut separator: S,
-) -> impl Parser<I, C, E>
+pub fn separated<Input, Output, Accumulator, Sep, Error, ParseNext, SepParser>(
+    occurrences: impl Into<Range>,
+    mut parser: ParseNext,
+    mut separator: SepParser,
+) -> impl Parser<Input, Accumulator, Error>
 where
-    I: Stream,
-    C: Accumulate<O>,
-    P: Parser<I, O, E>,
-    S: Parser<I, O2, E>,
-    E: ParserError<I>,
+    Input: Stream,
+    Accumulator: Accumulate<Output>,
+    ParseNext: Parser<Input, Output, Error>,
+    SepParser: Parser<Input, Sep, Error>,
+    Error: ParserError<Input>,
 {
     let Range {
         start_inclusive,
         end_inclusive,
-    } = range.into();
-    trace("separated", move |input: &mut I| {
+    } = occurrences.into();
+    trace("separated", move |input: &mut Input| {
         match (start_inclusive, end_inclusive) {
             (0, None) => separated0_(&mut parser, &mut separator, input),
             (1, None) => separated1_(&mut parser, &mut separator, input),
@@ -732,51 +900,6 @@
     })
 }
 
-/// [`Accumulate`] the output of a parser, interleaved with `sep`
-///
-/// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
-/// [`cut_err`][crate::combinator::cut_err].
-///
-/// # Arguments
-/// * `parser` Parses the elements of the list.
-/// * `sep` Parses the separator between list elements.
-///
-/// # Example
-///
-/// ```rust
-/// # #[cfg(feature = "std")] {
-/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
-/// # use winnow::prelude::*;
-/// use winnow::combinator::separated0;
-/// use winnow::token::tag;
-///
-/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
-///   separated0("abc", "|").parse_peek(s)
-/// }
-///
-/// assert_eq!(parser("abc|abc|abc"), Ok(("", vec!["abc", "abc", "abc"])));
-/// assert_eq!(parser("abc123abc"), Ok(("123abc", vec!["abc"])));
-/// assert_eq!(parser("abc|def"), Ok(("|def", vec!["abc"])));
-/// assert_eq!(parser(""), Ok(("", vec![])));
-/// assert_eq!(parser("def|abc"), Ok(("def|abc", vec![])));
-/// # }
-/// ```
-#[doc(alias = "sep_by")]
-#[doc(alias = "separated_list0")]
-#[deprecated(since = "0.5.19", note = "Replaced with `combinator::separated`")]
-pub fn separated0<I, O, C, O2, E, P, S>(mut parser: P, mut sep: S) -> impl Parser<I, C, E>
-where
-    I: Stream,
-    C: Accumulate<O>,
-    P: Parser<I, O, E>,
-    S: Parser<I, O2, E>,
-    E: ParserError<I>,
-{
-    trace("separated0", move |i: &mut I| {
-        separated0_(&mut parser, &mut sep, i)
-    })
-}
-
 fn separated0_<I, O, C, O2, E, P, S>(
     parser: &mut P,
     separator: &mut S,
@@ -794,7 +917,7 @@
     let start = input.checkpoint();
     match parser.parse_next(input) {
         Err(ErrMode::Backtrack(_)) => {
-            input.reset(start);
+            input.reset(&start);
             return Ok(acc);
         }
         Err(e) => return Err(e),
@@ -808,7 +931,7 @@
         let len = input.eof_offset();
         match separator.parse_next(input) {
             Err(ErrMode::Backtrack(_)) => {
-                input.reset(start);
+                input.reset(&start);
                 return Ok(acc);
             }
             Err(e) => return Err(e),
@@ -823,7 +946,7 @@
 
                 match parser.parse_next(input) {
                     Err(ErrMode::Backtrack(_)) => {
-                        input.reset(start);
+                        input.reset(&start);
                         return Ok(acc);
                     }
                     Err(e) => return Err(e),
@@ -836,53 +959,6 @@
     }
 }
 
-/// [`Accumulate`] the output of a parser, interleaved with `sep`
-///
-/// Fails if the element parser does not produce at least one element.$
-///
-/// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
-/// [`cut_err`][crate::combinator::cut_err].
-///
-/// # Arguments
-/// * `sep` Parses the separator between list elements.
-/// * `f` Parses the elements of the list.
-///
-/// # Example
-///
-/// ```rust
-/// # #[cfg(feature = "std")] {
-/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
-/// # use winnow::prelude::*;
-/// use winnow::combinator::separated1;
-/// use winnow::token::tag;
-///
-/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
-///   separated1("abc", "|").parse_peek(s)
-/// }
-///
-/// assert_eq!(parser("abc|abc|abc"), Ok(("", vec!["abc", "abc", "abc"])));
-/// assert_eq!(parser("abc123abc"), Ok(("123abc", vec!["abc"])));
-/// assert_eq!(parser("abc|def"), Ok(("|def", vec!["abc"])));
-/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
-/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Tag))));
-/// # }
-/// ```
-#[doc(alias = "sep_by1")]
-#[doc(alias = "separated_list1")]
-#[deprecated(since = "0.5.19", note = "Replaced with `combinator::separated`")]
-pub fn separated1<I, O, C, O2, E, P, S>(mut parser: P, mut sep: S) -> impl Parser<I, C, E>
-where
-    I: Stream,
-    C: Accumulate<O>,
-    P: Parser<I, O, E>,
-    S: Parser<I, O2, E>,
-    E: ParserError<I>,
-{
-    trace("separated1", move |i: &mut I| {
-        separated1_(&mut parser, &mut sep, i)
-    })
-}
-
 fn separated1_<I, O, C, O2, E, P, S>(
     parser: &mut P,
     separator: &mut S,
@@ -910,7 +986,7 @@
         let len = input.eof_offset();
         match separator.parse_next(input) {
             Err(ErrMode::Backtrack(_)) => {
-                input.reset(start);
+                input.reset(&start);
                 return Ok(acc);
             }
             Err(e) => return Err(e),
@@ -925,7 +1001,7 @@
 
                 match parser.parse_next(input) {
                     Err(ErrMode::Backtrack(_)) => {
-                        input.reset(start);
+                        input.reset(&start);
                         return Ok(acc);
                     }
                     Err(e) => return Err(e),
@@ -957,9 +1033,10 @@
         return Ok(acc);
     }
 
+    let start = input.checkpoint();
     match parser.parse_next(input) {
         Err(e) => {
-            return Err(e.append(input, ErrorKind::Many));
+            return Err(e.append(input, &start, ErrorKind::Many));
         }
         Ok(o) => {
             acc.accumulate(o);
@@ -967,10 +1044,11 @@
     }
 
     for _ in 1..count {
+        let start = input.checkpoint();
         let len = input.eof_offset();
         match separator.parse_next(input) {
             Err(e) => {
-                return Err(e.append(input, ErrorKind::Many));
+                return Err(e.append(input, &start, ErrorKind::Many));
             }
             Ok(_) => {
                 // infinite loop check
@@ -983,7 +1061,7 @@
 
                 match parser.parse_next(input) {
                     Err(e) => {
-                        return Err(e.append(input, ErrorKind::Many));
+                        return Err(e.append(input, &start, ErrorKind::Many));
                     }
                     Ok(o) => {
                         acc.accumulate(o);
@@ -1011,7 +1089,10 @@
     E: ParserError<I>,
 {
     if min > max {
-        return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many)));
+        return Err(ErrMode::assert(
+            input,
+            "range should be ascending, rather than descending",
+        ));
     }
 
     let mut acc = C::initial(Some(min));
@@ -1020,10 +1101,10 @@
     match parser.parse_next(input) {
         Err(ErrMode::Backtrack(e)) => {
             if min == 0 {
-                input.reset(start);
+                input.reset(&start);
                 return Ok(acc);
             } else {
-                return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many)));
+                return Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Many)));
             }
         }
         Err(e) => return Err(e),
@@ -1038,9 +1119,9 @@
         match separator.parse_next(input) {
             Err(ErrMode::Backtrack(e)) => {
                 if index < min {
-                    return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many)));
+                    return Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Many)));
                 } else {
-                    input.reset(start);
+                    input.reset(&start);
                     return Ok(acc);
                 }
             }
@@ -1059,9 +1140,13 @@
                 match parser.parse_next(input) {
                     Err(ErrMode::Backtrack(e)) => {
                         if index < min {
-                            return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many)));
+                            return Err(ErrMode::Backtrack(e.append(
+                                input,
+                                &start,
+                                ErrorKind::Many,
+                            )));
                         } else {
-                            input.reset(start);
+                            input.reset(&start);
                             return Ok(acc);
                         }
                     }
@@ -1097,22 +1182,22 @@
 /// }
 ///
 /// assert_eq!(parser("9-3-5"), Ok(("", 1)));
-/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
-/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Slice))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Verify))));
 /// ```
-pub fn separated_foldl1<I, O, O2, E, P, S, Op>(
-    mut parser: P,
-    mut sep: S,
+pub fn separated_foldl1<Input, Output, Sep, Error, ParseNext, SepParser, Op>(
+    mut parser: ParseNext,
+    mut sep: SepParser,
     mut op: Op,
-) -> impl Parser<I, O, E>
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    P: Parser<I, O, E>,
-    S: Parser<I, O2, E>,
-    E: ParserError<I>,
-    Op: FnMut(O, O2, O) -> O,
+    Input: Stream,
+    ParseNext: Parser<Input, Output, Error>,
+    SepParser: Parser<Input, Sep, Error>,
+    Error: ParserError<Input>,
+    Op: FnMut(Output, Sep, Output) -> Output,
 {
-    trace("separated_foldl1", move |i: &mut I| {
+    trace("separated_foldl1", move |i: &mut Input| {
         let mut ol = parser.parse_next(i)?;
 
         loop {
@@ -1120,7 +1205,7 @@
             let len = i.eof_offset();
             match sep.parse_next(i) {
                 Err(ErrMode::Backtrack(_)) => {
-                    i.reset(start);
+                    i.reset(&start);
                     return Ok(ol);
                 }
                 Err(e) => return Err(e),
@@ -1132,7 +1217,7 @@
 
                     match parser.parse_next(i) {
                         Err(ErrMode::Backtrack(_)) => {
-                            i.reset(start);
+                            i.reset(&start);
                             return Ok(ol);
                         }
                         Err(e) => return Err(e),
@@ -1165,25 +1250,25 @@
 ///
 /// assert_eq!(parser("2^3^2"), Ok(("", 512)));
 /// assert_eq!(parser("2"), Ok(("", 2)));
-/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
-/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Slice))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Verify))));
 /// ```
 #[cfg(feature = "alloc")]
-pub fn separated_foldr1<I, O, O2, E, P, S, Op>(
-    mut parser: P,
-    mut sep: S,
+pub fn separated_foldr1<Input, Output, Sep, Error, ParseNext, SepParser, Op>(
+    mut parser: ParseNext,
+    mut sep: SepParser,
     mut op: Op,
-) -> impl Parser<I, O, E>
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    P: Parser<I, O, E>,
-    S: Parser<I, O2, E>,
-    E: ParserError<I>,
-    Op: FnMut(O, O2, O) -> O,
+    Input: Stream,
+    ParseNext: Parser<Input, Output, Error>,
+    SepParser: Parser<Input, Sep, Error>,
+    Error: ParserError<Input>,
+    Op: FnMut(Output, Sep, Output) -> Output,
 {
-    trace("separated_foldr1", move |i: &mut I| {
+    trace("separated_foldr1", move |i: &mut Input| {
         let ol = parser.parse_next(i)?;
-        let all: crate::lib::std::vec::Vec<(O2, O)> =
+        let all: crate::lib::std::vec::Vec<(Sep, Output)> =
             repeat(0.., (sep.by_ref(), parser.by_ref())).parse_next(i)?;
         if let Some((s, or)) = all
             .into_iter()
@@ -1202,17 +1287,12 @@
 ///
 /// This parser fails if the input runs out before the given slice is full.
 ///
-/// # Arguments
-/// * `f` The parser to apply.
-/// * `buf` The slice to fill
-///
 /// # Example
 ///
 /// ```rust
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
 /// use winnow::combinator::fill;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: &str) -> IResult<&str, [&str; 2]> {
 ///   let mut buf = ["", ""];
@@ -1226,20 +1306,24 @@
 /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
 /// assert_eq!(parser("abcabcabc"), Ok(("abc", ["abc", "abc"])));
 /// ```
-pub fn fill<'a, I, O, E, F>(mut f: F, buf: &'a mut [O]) -> impl Parser<I, (), E> + 'a
+pub fn fill<'i, Input, Output, Error, ParseNext>(
+    mut parser: ParseNext,
+    buf: &'i mut [Output],
+) -> impl Parser<Input, (), Error> + 'i
 where
-    I: Stream + 'a,
-    F: Parser<I, O, E> + 'a,
-    E: ParserError<I> + 'a,
+    Input: Stream + 'i,
+    ParseNext: Parser<Input, Output, Error> + 'i,
+    Error: ParserError<Input> + 'i,
 {
-    trace("fill", move |i: &mut I| {
+    trace("fill", move |i: &mut Input| {
         for elem in buf.iter_mut() {
-            match f.parse_next(i) {
+            let start = i.checkpoint();
+            match parser.parse_next(i) {
                 Ok(o) => {
                     *elem = o;
                 }
                 Err(e) => {
-                    return Err(e.append(i, ErrorKind::Many));
+                    return Err(e.append(i, &start, ErrorKind::Many));
                 }
             }
         }
@@ -1248,25 +1332,6 @@
     })
 }
 
-/// Deprecated, replaced with [`Repeat::fold`]
-#[deprecated(since = "0.5.36", note = "Replaced with `repeat(...).fold(...)`")]
-#[inline(always)]
-pub fn fold_repeat<I, O, E, F, G, H, R>(
-    range: impl Into<Range>,
-    f: F,
-    init: H,
-    g: G,
-) -> impl Parser<I, R, E>
-where
-    I: Stream,
-    F: Parser<I, O, E>,
-    G: FnMut(R, O) -> R,
-    H: FnMut() -> R,
-    E: ParserError<I>,
-{
-    repeat(range, f).fold(init, g)
-}
-
 fn fold_repeat0_<I, O, E, F, G, H, R>(
     f: &mut F,
     init: &mut H,
@@ -1298,7 +1363,7 @@
                 res = g(res, o);
             }
             Err(ErrMode::Backtrack(_)) => {
-                input.reset(start);
+                input.reset(&start);
                 return Ok(res);
             }
             Err(e) => {
@@ -1333,7 +1398,7 @@
                 let len = input.eof_offset();
                 match f.parse_next(input) {
                     Err(ErrMode::Backtrack(_)) => {
-                        input.reset(start);
+                        input.reset(&start);
                         break;
                     }
                     Err(e) => return Err(e),
@@ -1372,7 +1437,10 @@
     E: ParserError<I>,
 {
     if min > max {
-        return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many)));
+        return Err(ErrMode::assert(
+            input,
+            "range should be ascending, rather than descending",
+        ));
     }
 
     let mut acc = init();
@@ -1394,9 +1462,145 @@
             //FInputXMError: handle failure properly
             Err(ErrMode::Backtrack(err)) => {
                 if count < min {
-                    return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Many)));
+                    return Err(ErrMode::Backtrack(err.append(
+                        input,
+                        &start,
+                        ErrorKind::Many,
+                    )));
                 } else {
-                    input.reset(start);
+                    input.reset(&start);
+                    break;
+                }
+            }
+            Err(e) => return Err(e),
+        }
+    }
+
+    Ok(acc)
+}
+
+#[inline(always)]
+fn verify_fold_m_n<I, O, E, F, G, H, R>(
+    min: usize,
+    max: usize,
+    parse: &mut F,
+    init: &mut H,
+    fold: &mut G,
+    input: &mut I,
+) -> PResult<R, E>
+where
+    I: Stream,
+    F: Parser<I, O, E>,
+    G: FnMut(R, O) -> Option<R>,
+    H: FnMut() -> R,
+    E: ParserError<I>,
+{
+    if min > max {
+        return Err(ErrMode::assert(
+            input,
+            "range should be ascending, rather than descending",
+        ));
+    }
+
+    let mut acc = init();
+    for count in 0..max {
+        let start = input.checkpoint();
+        let len = input.eof_offset();
+        match parse.parse_next(input) {
+            Ok(value) => {
+                // infinite loop check: the parser must always consume
+                if input.eof_offset() == len {
+                    return Err(ErrMode::assert(
+                        input,
+                        "`repeat` parsers must always consume",
+                    ));
+                }
+
+                let Some(tmp) = fold(acc, value) else {
+                    input.reset(&start);
+                    let res = Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
+                    super::debug::trace_result("verify_fold", &res);
+                    return res;
+                };
+                acc = tmp;
+            }
+            //FInputXMError: handle failure properly
+            Err(ErrMode::Backtrack(err)) => {
+                if count < min {
+                    return Err(ErrMode::Backtrack(err.append(
+                        input,
+                        &start,
+                        ErrorKind::Many,
+                    )));
+                } else {
+                    input.reset(&start);
+                    break;
+                }
+            }
+            Err(e) => return Err(e),
+        }
+    }
+
+    Ok(acc)
+}
+
+#[inline(always)]
+fn try_fold_m_n<I, O, E, F, G, H, R, GE>(
+    min: usize,
+    max: usize,
+    parse: &mut F,
+    init: &mut H,
+    fold: &mut G,
+    input: &mut I,
+) -> PResult<R, E>
+where
+    I: Stream,
+    F: Parser<I, O, E>,
+    G: FnMut(R, O) -> Result<R, GE>,
+    H: FnMut() -> R,
+    E: ParserError<I> + FromExternalError<I, GE>,
+{
+    if min > max {
+        return Err(ErrMode::assert(
+            input,
+            "range should be ascending, rather than descending",
+        ));
+    }
+
+    let mut acc = init();
+    for count in 0..max {
+        let start = input.checkpoint();
+        let len = input.eof_offset();
+        match parse.parse_next(input) {
+            Ok(value) => {
+                // infinite loop check: the parser must always consume
+                if input.eof_offset() == len {
+                    return Err(ErrMode::assert(
+                        input,
+                        "`repeat` parsers must always consume",
+                    ));
+                }
+
+                match fold(acc, value) {
+                    Ok(tmp) => acc = tmp,
+                    Err(e) => {
+                        input.reset(&start);
+                        let res = Err(ErrMode::from_external_error(input, ErrorKind::Verify, e));
+                        super::debug::trace_result("try_fold", &res);
+                        return res;
+                    }
+                }
+            }
+            //FInputXMError: handle failure properly
+            Err(ErrMode::Backtrack(err)) => {
+                if count < min {
+                    return Err(ErrMode::Backtrack(err.append(
+                        input,
+                        &start,
+                        ErrorKind::Many,
+                    )));
+                } else {
+                    input.reset(&start);
                     break;
                 }
             }
diff --git a/crates/winnow/src/combinator/parser.rs b/crates/winnow/src/combinator/parser.rs
deleted file mode 100644
index 9ffdb3c..0000000
--- a/crates/winnow/src/combinator/parser.rs
+++ /dev/null
@@ -1,1097 +0,0 @@
-use crate::combinator::trace;
-use crate::combinator::trace_result;
-#[cfg(feature = "unstable-recover")]
-use crate::error::FromRecoverableError;
-use crate::error::{AddContext, ErrMode, ErrorKind, FromExternalError, ParserError};
-use crate::lib::std::borrow::Borrow;
-use crate::lib::std::ops::Range;
-#[cfg(feature = "unstable-recover")]
-use crate::stream::Recover;
-use crate::stream::StreamIsPartial;
-use crate::stream::{Location, Stream};
-use crate::*;
-
-/// Implementation of [`Parser::by_ref`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct ByRef<'p, P> {
-    p: &'p mut P,
-}
-
-impl<'p, P> ByRef<'p, P> {
-    #[inline(always)]
-    pub(crate) fn new(p: &'p mut P) -> Self {
-        Self { p }
-    }
-}
-
-impl<'p, I, O, E, P> Parser<I, O, E> for ByRef<'p, P>
-where
-    P: Parser<I, O, E>,
-{
-    #[inline(always)]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
-        self.p.parse_next(i)
-    }
-}
-
-/// Implementation of [`Parser::map`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Map<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> O2,
-{
-    parser: F,
-    map: G,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, G, I, O, O2, E> Map<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> O2,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F, map: G) -> Self {
-        Self {
-            parser,
-            map,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, G, I, O, O2, E> Parser<I, O2, E> for Map<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> O2,
-{
-    #[inline]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
-        match self.parser.parse_next(i) {
-            Err(e) => Err(e),
-            Ok(o) => Ok((self.map)(o)),
-        }
-    }
-}
-
-/// Implementation of [`Parser::try_map`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct TryMap<F, G, I, O, O2, E, E2>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> Result<O2, E2>,
-    I: Stream,
-    E: FromExternalError<I, E2>,
-{
-    parser: F,
-    map: G,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-    e2: core::marker::PhantomData<E2>,
-}
-
-impl<F, G, I, O, O2, E, E2> TryMap<F, G, I, O, O2, E, E2>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> Result<O2, E2>,
-    I: Stream,
-    E: FromExternalError<I, E2>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F, map: G) -> Self {
-        Self {
-            parser,
-            map,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-            e2: Default::default(),
-        }
-    }
-}
-
-impl<F, G, I, O, O2, E, E2> Parser<I, O2, E> for TryMap<F, G, I, O, O2, E, E2>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> Result<O2, E2>,
-    I: Stream,
-    E: FromExternalError<I, E2>,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
-        let start = input.checkpoint();
-        let o = self.parser.parse_next(input)?;
-        let res = (self.map)(o).map_err(|err| {
-            input.reset(start);
-            ErrMode::from_external_error(input, ErrorKind::Verify, err)
-        });
-        trace_result("verify", &res);
-        res
-    }
-}
-
-/// Implementation of [`Parser::verify_map`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct VerifyMap<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> Option<O2>,
-    I: Stream,
-    E: ParserError<I>,
-{
-    parser: F,
-    map: G,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, G, I, O, O2, E> VerifyMap<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> Option<O2>,
-    I: Stream,
-    E: ParserError<I>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F, map: G) -> Self {
-        Self {
-            parser,
-            map,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, G, I, O, O2, E> Parser<I, O2, E> for VerifyMap<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> Option<O2>,
-    I: Stream,
-    E: ParserError<I>,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
-        let start = input.checkpoint();
-        let o = self.parser.parse_next(input)?;
-        let res = (self.map)(o).ok_or_else(|| {
-            input.reset(start);
-            ErrMode::from_error_kind(input, ErrorKind::Verify)
-        });
-        trace_result("verify", &res);
-        res
-    }
-}
-
-/// Implementation of [`Parser::and_then`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct AndThen<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: Parser<O, O2, E>,
-    O: StreamIsPartial,
-    I: Stream,
-{
-    outer: F,
-    inner: G,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, G, I, O, O2, E> AndThen<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: Parser<O, O2, E>,
-    O: StreamIsPartial,
-    I: Stream,
-{
-    #[inline(always)]
-    pub(crate) fn new(outer: F, inner: G) -> Self {
-        Self {
-            outer,
-            inner,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, G, I, O, O2, E> Parser<I, O2, E> for AndThen<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: Parser<O, O2, E>,
-    O: StreamIsPartial,
-    I: Stream,
-{
-    #[inline(always)]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
-        let start = i.checkpoint();
-        let mut o = self.outer.parse_next(i)?;
-        let _ = o.complete();
-        let o2 = self.inner.parse_next(&mut o).map_err(|err| {
-            i.reset(start);
-            err
-        })?;
-        Ok(o2)
-    }
-}
-
-/// Implementation of [`Parser::parse_to`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct ParseTo<P, I, O, O2, E>
-where
-    P: Parser<I, O, E>,
-    I: Stream,
-    O: crate::stream::ParseSlice<O2>,
-    E: ParserError<I>,
-{
-    p: P,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<P, I, O, O2, E> ParseTo<P, I, O, O2, E>
-where
-    P: Parser<I, O, E>,
-    I: Stream,
-    O: crate::stream::ParseSlice<O2>,
-    E: ParserError<I>,
-{
-    #[inline(always)]
-    pub(crate) fn new(p: P) -> Self {
-        Self {
-            p,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<P, I, O, O2, E> Parser<I, O2, E> for ParseTo<P, I, O, O2, E>
-where
-    P: Parser<I, O, E>,
-    I: Stream,
-    O: crate::stream::ParseSlice<O2>,
-    E: ParserError<I>,
-{
-    #[inline]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
-        let start = i.checkpoint();
-        let o = self.p.parse_next(i)?;
-        let res = o.parse_slice().ok_or_else(|| {
-            i.reset(start);
-            ErrMode::from_error_kind(i, ErrorKind::Verify)
-        });
-        trace_result("verify", &res);
-        res
-    }
-}
-
-/// Implementation of [`Parser::flat_map`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct FlatMap<F, G, H, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> H,
-    H: Parser<I, O2, E>,
-{
-    f: F,
-    g: G,
-    h: core::marker::PhantomData<H>,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, G, H, I, O, O2, E> FlatMap<F, G, H, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> H,
-    H: Parser<I, O2, E>,
-{
-    #[inline(always)]
-    pub(crate) fn new(f: F, g: G) -> Self {
-        Self {
-            f,
-            g,
-            h: Default::default(),
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, G, H, I, O, O2, E> Parser<I, O2, E> for FlatMap<F, G, H, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(O) -> H,
-    H: Parser<I, O2, E>,
-{
-    #[inline(always)]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
-        let o = self.f.parse_next(i)?;
-        (self.g)(o).parse_next(i)
-    }
-}
-
-/// Implementation of [`Parser::complete_err`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct CompleteErr<F> {
-    f: F,
-}
-
-impl<F> CompleteErr<F> {
-    #[inline(always)]
-    pub(crate) fn new(f: F) -> Self {
-        Self { f }
-    }
-}
-
-impl<F, I, O, E> Parser<I, O, E> for CompleteErr<F>
-where
-    I: Stream,
-    F: Parser<I, O, E>,
-    E: ParserError<I>,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<O, E> {
-        trace("complete_err", |input: &mut I| {
-            match (self.f).parse_next(input) {
-                Err(ErrMode::Incomplete(_)) => {
-                    Err(ErrMode::from_error_kind(input, ErrorKind::Complete))
-                }
-                rest => rest,
-            }
-        })
-        .parse_next(input)
-    }
-}
-
-/// Implementation of [`Parser::verify`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Verify<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(&O2) -> bool,
-    I: Stream,
-    O: Borrow<O2>,
-    O2: ?Sized,
-    E: ParserError<I>,
-{
-    parser: F,
-    filter: G,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, G, I, O, O2, E> Verify<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(&O2) -> bool,
-    I: Stream,
-    O: Borrow<O2>,
-    O2: ?Sized,
-    E: ParserError<I>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F, filter: G) -> Self {
-        Self {
-            parser,
-            filter,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, G, I, O, O2, E> Parser<I, O, E> for Verify<F, G, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    G: FnMut(&O2) -> bool,
-    I: Stream,
-    O: Borrow<O2>,
-    O2: ?Sized,
-    E: ParserError<I>,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<O, E> {
-        let start = input.checkpoint();
-        let o = self.parser.parse_next(input)?;
-        let res = (self.filter)(o.borrow()).then_some(o).ok_or_else(|| {
-            input.reset(start);
-            ErrMode::from_error_kind(input, ErrorKind::Verify)
-        });
-        trace_result("verify", &res);
-        res
-    }
-}
-
-/// Implementation of [`Parser::value`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Value<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O2: Clone,
-{
-    parser: F,
-    val: O2,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, O2, E> Value<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O2: Clone,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F, val: O2) -> Self {
-        Self {
-            parser,
-            val,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, O2, E> Parser<I, O2, E> for Value<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O2: Clone,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
-        (self.parser).parse_next(input).map(|_| self.val.clone())
-    }
-}
-
-/// Implementation of [`Parser::default_value`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct DefaultValue<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O2: core::default::Default,
-{
-    parser: F,
-    o2: core::marker::PhantomData<O2>,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, O2, E> DefaultValue<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O2: core::default::Default,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            o2: Default::default(),
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, O2, E> Parser<I, O2, E> for DefaultValue<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O2: core::default::Default,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
-        (self.parser).parse_next(input).map(|_| O2::default())
-    }
-}
-
-/// Implementation of [`Parser::void`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Void<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, E> Void<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, E> Parser<I, (), E> for Void<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-{
-    #[inline(always)]
-    fn parse_next(&mut self, input: &mut I) -> PResult<(), E> {
-        (self.parser).parse_next(input).map(|_| ())
-    }
-}
-
-/// Implementation of [`Parser::recognize`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Recognize<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, E> Recognize<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<I, O, E, F> Parser<I, <I as Stream>::Slice, E> for Recognize<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        let checkpoint = input.checkpoint();
-        match (self.parser).parse_next(input) {
-            Ok(_) => {
-                let offset = input.offset_from(&checkpoint);
-                input.reset(checkpoint);
-                let recognized = input.next_slice(offset);
-                Ok(recognized)
-            }
-            Err(e) => Err(e),
-        }
-    }
-}
-
-/// Implementation of [`Parser::with_recognized`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct WithRecognized<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, E> WithRecognized<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, E> Parser<I, (O, <I as Stream>::Slice), E> for WithRecognized<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<(O, <I as Stream>::Slice), E> {
-        let checkpoint = input.checkpoint();
-        match (self.parser).parse_next(input) {
-            Ok(result) => {
-                let offset = input.offset_from(&checkpoint);
-                input.reset(checkpoint);
-                let recognized = input.next_slice(offset);
-                Ok((result, recognized))
-            }
-            Err(e) => Err(e),
-        }
-    }
-}
-
-/// Implementation of [`Parser::span`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Span<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream + Location,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, E> Span<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream + Location,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<I, O, E, F> Parser<I, Range<usize>, E> for Span<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream + Location,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<Range<usize>, E> {
-        let start = input.location();
-        self.parser.parse_next(input).map(move |_| {
-            let end = input.location();
-            start..end
-        })
-    }
-}
-
-/// Implementation of [`Parser::with_span`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct WithSpan<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream + Location,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, E> WithSpan<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream + Location,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, E> Parser<I, (O, Range<usize>), E> for WithSpan<F, I, O, E>
-where
-    F: Parser<I, O, E>,
-    I: Stream + Location,
-{
-    #[inline]
-    fn parse_next(&mut self, input: &mut I) -> PResult<(O, Range<usize>), E> {
-        let start = input.location();
-        self.parser.parse_next(input).map(move |output| {
-            let end = input.location();
-            (output, (start..end))
-        })
-    }
-}
-
-/// Implementation of [`Parser::output_into`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct OutputInto<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O: Into<O2>,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    o2: core::marker::PhantomData<O2>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, O2, E> OutputInto<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O: Into<O2>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            o2: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, O2, E> Parser<I, O2, E> for OutputInto<F, I, O, O2, E>
-where
-    F: Parser<I, O, E>,
-    O: Into<O2>,
-{
-    #[inline]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
-        self.parser.parse_next(i).map(|o| o.into())
-    }
-}
-
-/// Implementation of [`Parser::err_into`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct ErrInto<F, I, O, E, E2>
-where
-    F: Parser<I, O, E>,
-    E: Into<E2>,
-{
-    parser: F,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-    e2: core::marker::PhantomData<E2>,
-}
-
-impl<F, I, O, E, E2> ErrInto<F, I, O, E, E2>
-where
-    F: Parser<I, O, E>,
-    E: Into<E2>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F) -> Self {
-        Self {
-            parser,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-            e2: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, E, E2> Parser<I, O, E2> for ErrInto<F, I, O, E, E2>
-where
-    F: Parser<I, O, E>,
-    E: Into<E2>,
-{
-    #[inline]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O, E2> {
-        match self.parser.parse_next(i) {
-            Ok(ok) => Ok(ok),
-            Err(ErrMode::Backtrack(e)) => Err(ErrMode::Backtrack(e.into())),
-            Err(ErrMode::Cut(e)) => Err(ErrMode::Cut(e.into())),
-            Err(ErrMode::Incomplete(e)) => Err(ErrMode::Incomplete(e)),
-        }
-    }
-}
-
-/// Implementation of [`Parser::context`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct Context<F, I, O, E, C>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-    E: AddContext<I, C>,
-    C: Clone + crate::lib::std::fmt::Debug,
-{
-    parser: F,
-    context: C,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-impl<F, I, O, E, C> Context<F, I, O, E, C>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-    E: AddContext<I, C>,
-    C: Clone + crate::lib::std::fmt::Debug,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: F, context: C) -> Self {
-        Self {
-            parser,
-            context,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-impl<F, I, O, E, C> Parser<I, O, E> for Context<F, I, O, E, C>
-where
-    F: Parser<I, O, E>,
-    I: Stream,
-    E: AddContext<I, C>,
-    C: Clone + crate::lib::std::fmt::Debug,
-{
-    #[inline]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
-        #[cfg(feature = "debug")]
-        let name = format!("context={:?}", self.context);
-        #[cfg(not(feature = "debug"))]
-        let name = "context";
-        trace(name, move |i: &mut I| {
-            (self.parser)
-                .parse_next(i)
-                .map_err(|err| err.add_context(i, self.context.clone()))
-        })
-        .parse_next(i)
-    }
-}
-
-/// Implementation of [`Parser::retry_after`]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-#[cfg(feature = "unstable-recover")]
-pub struct RetryAfter<P, R, I, O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    parser: P,
-    recover: R,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-#[cfg(feature = "unstable-recover")]
-impl<P, R, I, O, E> RetryAfter<P, R, I, O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: P, recover: R) -> Self {
-        Self {
-            parser,
-            recover,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-#[cfg(feature = "unstable-recover")]
-impl<P, R, I, O, E> Parser<I, O, E> for RetryAfter<P, R, I, O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    #[inline(always)]
-    fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
-        if I::is_recovery_supported() {
-            retry_after_inner(&mut self.parser, &mut self.recover, i)
-        } else {
-            self.parser.parse_next(i)
-        }
-    }
-}
-
-#[cfg(feature = "unstable-recover")]
-fn retry_after_inner<P, R, I, O, E>(parser: &mut P, recover: &mut R, i: &mut I) -> PResult<O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    loop {
-        let token_start = i.checkpoint();
-        let mut err = match parser.parse_next(i) {
-            Ok(o) => {
-                return Ok(o);
-            }
-            Err(ErrMode::Incomplete(e)) => return Err(ErrMode::Incomplete(e)),
-            Err(err) => err,
-        };
-        let err_start = i.checkpoint();
-        let err_start_eof_offset = i.eof_offset();
-        if recover.parse_next(i).is_ok() {
-            let i_eof_offset = i.eof_offset();
-            if err_start_eof_offset == i_eof_offset {
-                // Didn't advance so bubble the error up
-            } else if let Err(err_) = i.record_err(&token_start, &err_start, err) {
-                err = err_;
-            } else {
-                continue;
-            }
-        }
-
-        i.reset(err_start.clone());
-        err = err.map(|err| E::from_recoverable_error(&token_start, &err_start, i, err));
-        return Err(err);
-    }
-}
-
-/// Implementation of [`Parser::resume_after`]
-#[cfg(feature = "unstable-recover")]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
-pub struct ResumeAfter<P, R, I, O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    parser: P,
-    recover: R,
-    i: core::marker::PhantomData<I>,
-    o: core::marker::PhantomData<O>,
-    e: core::marker::PhantomData<E>,
-}
-
-#[cfg(feature = "unstable-recover")]
-impl<P, R, I, O, E> ResumeAfter<P, R, I, O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    #[inline(always)]
-    pub(crate) fn new(parser: P, recover: R) -> Self {
-        Self {
-            parser,
-            recover,
-            i: Default::default(),
-            o: Default::default(),
-            e: Default::default(),
-        }
-    }
-}
-
-#[cfg(feature = "unstable-recover")]
-impl<P, R, I, O, E> Parser<I, Option<O>, E> for ResumeAfter<P, R, I, O, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    #[inline(always)]
-    fn parse_next(&mut self, i: &mut I) -> PResult<Option<O>, E> {
-        if I::is_recovery_supported() {
-            resume_after_inner(&mut self.parser, &mut self.recover, i)
-        } else {
-            self.parser.parse_next(i).map(Some)
-        }
-    }
-}
-
-#[cfg(feature = "unstable-recover")]
-fn resume_after_inner<P, R, I, O, E>(
-    parser: &mut P,
-    recover: &mut R,
-    i: &mut I,
-) -> PResult<Option<O>, E>
-where
-    P: Parser<I, O, E>,
-    R: Parser<I, (), E>,
-    I: Stream,
-    I: Recover<E>,
-    E: FromRecoverableError<I, E>,
-{
-    let token_start = i.checkpoint();
-    let mut err = match parser.parse_next(i) {
-        Ok(o) => {
-            return Ok(Some(o));
-        }
-        Err(ErrMode::Incomplete(e)) => return Err(ErrMode::Incomplete(e)),
-        Err(err) => err,
-    };
-    let err_start = i.checkpoint();
-    if recover.parse_next(i).is_ok() {
-        if let Err(err_) = i.record_err(&token_start, &err_start, err) {
-            err = err_;
-        } else {
-            return Ok(None);
-        }
-    }
-
-    i.reset(err_start.clone());
-    err = err.map(|err| E::from_recoverable_error(&token_start, &err_start, i, err));
-    Err(err)
-}
diff --git a/crates/winnow/src/combinator/sequence.rs b/crates/winnow/src/combinator/sequence.rs
index 0f2e633..b3b2711 100644
--- a/crates/winnow/src/combinator/sequence.rs
+++ b/crates/winnow/src/combinator/sequence.rs
@@ -8,10 +8,6 @@
 
 /// Sequence two parsers, only returning the output from the second.
 ///
-/// # Arguments
-/// * `first` The opening parser.
-/// * `second` The second parser to get object.
-///
 /// See also [`seq`] to generalize this across any number of fields.
 ///
 /// # Example
@@ -21,7 +17,6 @@
 /// # use winnow::prelude::*;
 /// # use winnow::error::Needed::Size;
 /// use winnow::combinator::preceded;
-/// use winnow::token::tag;
 ///
 /// let mut parser = preceded("abc", "efg");
 ///
@@ -31,27 +26,24 @@
 /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
 /// ```
 #[doc(alias = "ignore_then")]
-pub fn preceded<I, O1, O2, E: ParserError<I>, F, G>(
-    mut first: F,
-    mut second: G,
-) -> impl Parser<I, O2, E>
+pub fn preceded<Input, Ignored, Output, Error, IgnoredParser, ParseNext>(
+    mut ignored: IgnoredParser,
+    mut parser: ParseNext,
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    F: Parser<I, O1, E>,
-    G: Parser<I, O2, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    IgnoredParser: Parser<Input, Ignored, Error>,
+    ParseNext: Parser<Input, Output, Error>,
 {
-    trace("preceded", move |input: &mut I| {
-        let _ = first.parse_next(input)?;
-        second.parse_next(input)
+    trace("preceded", move |input: &mut Input| {
+        let _ = ignored.parse_next(input)?;
+        parser.parse_next(input)
     })
 }
 
 /// Sequence two parsers, only returning the output of the first.
 ///
-/// # Arguments
-/// * `first` The first parser to apply.
-/// * `second` The second parser to match an object.
-///
 /// See also [`seq`] to generalize this across any number of fields.
 ///
 /// # Example
@@ -61,7 +53,6 @@
 /// # use winnow::prelude::*;
 /// # use winnow::error::Needed::Size;
 /// use winnow::combinator::terminated;
-/// use winnow::token::tag;
 ///
 /// let mut parser = terminated("abc", "efg");
 ///
@@ -71,28 +62,24 @@
 /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
 /// ```
 #[doc(alias = "then_ignore")]
-pub fn terminated<I, O1, O2, E: ParserError<I>, F, G>(
-    mut first: F,
-    mut second: G,
-) -> impl Parser<I, O1, E>
+pub fn terminated<Input, Output, Ignored, Error, ParseNext, IgnoredParser>(
+    mut parser: ParseNext,
+    mut ignored: IgnoredParser,
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    F: Parser<I, O1, E>,
-    G: Parser<I, O2, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    ParseNext: Parser<Input, Output, Error>,
+    IgnoredParser: Parser<Input, Ignored, Error>,
 {
-    trace("terminated", move |input: &mut I| {
-        let o1 = first.parse_next(input)?;
-        second.parse_next(input).map(|_| o1)
+    trace("terminated", move |input: &mut Input| {
+        let o = parser.parse_next(input)?;
+        ignored.parse_next(input).map(|_| o)
     })
 }
 
 /// Sequence three parsers, only returning the values of the first and third.
 ///
-/// # Arguments
-/// * `first` The first parser to apply.
-/// * `sep` The separator parser to apply.
-/// * `second` The second parser to apply.
-///
 /// See also [`seq`] to generalize this across any number of fields.
 ///
 /// # Example
@@ -102,7 +89,6 @@
 /// # use winnow::error::Needed::Size;
 /// # use winnow::prelude::*;
 /// use winnow::combinator::separated_pair;
-/// use winnow::token::tag;
 ///
 /// let mut parser = separated_pair("abc", "|", "efg");
 ///
@@ -111,18 +97,19 @@
 /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
 /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
 /// ```
-pub fn separated_pair<I, O1, O2, O3, E: ParserError<I>, F, G, H>(
-    mut first: F,
-    mut sep: G,
-    mut second: H,
-) -> impl Parser<I, (O1, O3), E>
+pub fn separated_pair<Input, O1, Sep, O2, Error, P1, SepParser, P2>(
+    mut first: P1,
+    mut sep: SepParser,
+    mut second: P2,
+) -> impl Parser<Input, (O1, O2), Error>
 where
-    I: Stream,
-    F: Parser<I, O1, E>,
-    G: Parser<I, O2, E>,
-    H: Parser<I, O3, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    P1: Parser<Input, O1, Error>,
+    SepParser: Parser<Input, Sep, Error>,
+    P2: Parser<Input, O2, Error>,
 {
-    trace("separated_pair", move |input: &mut I| {
+    trace("separated_pair", move |input: &mut Input| {
         let o1 = first.parse_next(input)?;
         let _ = sep.parse_next(input)?;
         second.parse_next(input).map(|o2| (o1, o2))
@@ -131,11 +118,6 @@
 
 /// Sequence three parsers, only returning the output of the second.
 ///
-/// # Arguments
-/// * `first` The first parser to apply and discard.
-/// * `second` The second parser to apply.
-/// * `third` The third parser to apply and discard.
-///
 /// See also [`seq`] to generalize this across any number of fields.
 ///
 /// # Example
@@ -145,7 +127,6 @@
 /// # use winnow::error::Needed::Size;
 /// # use winnow::prelude::*;
 /// use winnow::combinator::delimited;
-/// use winnow::token::tag;
 ///
 /// let mut parser = delimited("(", "abc", ")");
 ///
@@ -156,20 +137,30 @@
 /// ```
 #[doc(alias = "between")]
 #[doc(alias = "padded")]
-pub fn delimited<I, O1, O2, O3, E: ParserError<I>, F, G, H>(
-    mut first: F,
-    mut second: G,
-    mut third: H,
-) -> impl Parser<I, O2, E>
+pub fn delimited<
+    Input,
+    Ignored1,
+    Output,
+    Ignored2,
+    Error,
+    IgnoredParser1,
+    ParseNext,
+    IgnoredParser2,
+>(
+    mut ignored1: IgnoredParser1,
+    mut parser: ParseNext,
+    mut ignored2: IgnoredParser2,
+) -> impl Parser<Input, Output, Error>
 where
-    I: Stream,
-    F: Parser<I, O1, E>,
-    G: Parser<I, O2, E>,
-    H: Parser<I, O3, E>,
+    Input: Stream,
+    Error: ParserError<Input>,
+    IgnoredParser1: Parser<Input, Ignored1, Error>,
+    ParseNext: Parser<Input, Output, Error>,
+    IgnoredParser2: Parser<Input, Ignored2, Error>,
 {
-    trace("delimited", move |input: &mut I| {
-        let _ = first.parse_next(input)?;
-        let o2 = second.parse_next(input)?;
-        third.parse_next(input).map(|_| o2)
+    trace("delimited", move |input: &mut Input| {
+        let _ = ignored1.parse_next(input)?;
+        let o2 = parser.parse_next(input)?;
+        ignored2.parse_next(input).map(|_| o2)
     })
 }
diff --git a/crates/winnow/src/combinator/tests.rs b/crates/winnow/src/combinator/tests.rs
index 726b410..d0caf8c 100644
--- a/crates/winnow/src/combinator/tests.rs
+++ b/crates/winnow/src/combinator/tests.rs
@@ -9,6 +9,8 @@
 use crate::error::InputError;
 use crate::error::Needed;
 use crate::error::ParserError;
+#[cfg(feature = "alloc")]
+use crate::lib::std::borrow::ToOwned;
 use crate::stream::Stream;
 use crate::token::take;
 use crate::unpeek;
@@ -63,26 +65,6 @@
     assert_parse!(res_over, Ok((is_over, is_over)));
 }
 
-#[test]
-fn rest_on_slices() {
-    let input: &[u8] = &b"Hello, world!"[..];
-    let empty: &[u8] = &b""[..];
-    assert_parse!(rest.parse_peek(input), Ok((empty, input)));
-}
-
-#[test]
-fn rest_on_strs() {
-    let input: &str = "Hello, world!";
-    let empty: &str = "";
-    assert_parse!(rest.parse_peek(input), Ok((empty, input)));
-}
-
-#[test]
-fn rest_len_on_slices() {
-    let input: &[u8] = &b"Hello, world!"[..];
-    assert_parse!(rest_len.parse_peek(input), Ok((input, input.len())));
-}
-
 use crate::lib::std::convert::From;
 impl From<u32> for CustomError {
     fn from(_: u32) -> Self {
@@ -90,12 +72,12 @@
     }
 }
 
-impl<I> ParserError<I> for CustomError {
+impl<I: Stream> ParserError<I> for CustomError {
     fn from_error_kind(_: &I, _: ErrorKind) -> Self {
         CustomError
     }
 
-    fn append(self, _: &I, _: ErrorKind) -> Self {
+    fn append(self, _: &I, _: &<I as Stream>::Checkpoint, _: ErrorKind) -> Self {
         CustomError
     }
 }
@@ -186,20 +168,20 @@
 
 #[test]
 fn peek_test() {
-    fn peek_tag(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+    fn peek_literal(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
         peek("abcd").parse_peek(i)
     }
 
     assert_eq!(
-        peek_tag(Partial::new(&b"abcdef"[..])),
+        peek_literal(Partial::new(&b"abcdef"[..])),
         Ok((Partial::new(&b"abcdef"[..]), &b"abcd"[..]))
     );
     assert_eq!(
-        peek_tag(Partial::new(&b"ab"[..])),
+        peek_literal(Partial::new(&b"ab"[..])),
         Err(ErrMode::Incomplete(Needed::new(2)))
     );
     assert_eq!(
-        peek_tag(Partial::new(&b"xxx"[..])),
+        peek_literal(Partial::new(&b"xxx"[..])),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new(&b"xxx"[..]),
             ErrorKind::Tag
@@ -518,40 +500,36 @@
     #[cfg(feature = "alloc")]
     use crate::{
         error::ParserError,
-        lib::std::{
-            fmt::Debug,
-            string::{String, ToString},
-        },
+        lib::std::{fmt::Debug, string::String},
     };
 
     #[cfg(feature = "alloc")]
     #[derive(Debug, Clone, Eq, PartialEq)]
-    pub struct ErrorStr(String);
+    struct ErrorStr(String);
 
     #[cfg(feature = "alloc")]
     impl From<u32> for ErrorStr {
         fn from(i: u32) -> Self {
-            ErrorStr(format!("custom error code: {}", i))
+            ErrorStr(format!("custom error code: {i}"))
         }
     }
 
     #[cfg(feature = "alloc")]
     impl<'a> From<&'a str> for ErrorStr {
         fn from(i: &'a str) -> Self {
-            ErrorStr(format!("custom error message: {}", i))
+            ErrorStr(format!("custom error message: {i}"))
         }
     }
 
     #[cfg(feature = "alloc")]
-    impl<I: Debug> ParserError<I> for ErrorStr {
+    impl<I: Stream + Debug> ParserError<I> for ErrorStr {
         fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
-            ErrorStr(format!("custom error message: ({:?}, {:?})", input, kind))
+            ErrorStr(format!("custom error message: ({input:?}, {kind:?})"))
         }
 
-        fn append(self, input: &I, kind: ErrorKind) -> Self {
+        fn append(self, input: &I, _: &<I as Stream>::Checkpoint, kind: ErrorKind) -> Self {
             ErrorStr(format!(
-                "custom error message: ({:?}, {:?}) - {:?}",
-                input, kind, self
+                "custom error message: ({input:?}, {kind:?}) - {self:?}"
             ))
         }
     }
@@ -562,7 +540,7 @@
 
     #[allow(unused_variables)]
     fn dont_work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
-        Err(ErrMode::Backtrack(ErrorStr("abcd".to_string())))
+        Err(ErrMode::Backtrack(ErrorStr("abcd".to_owned())))
     }
 
     fn work2(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
@@ -594,7 +572,7 @@
         Err(ErrMode::Backtrack(error_node_position!(
             &a,
             ErrorKind::Alt,
-            ErrorStr("abcd".to_string())
+            ErrorStr("abcd".to_owned())
         )))
     );
     assert_eq!(alt2(a), Ok((&b""[..], a)));
@@ -672,6 +650,22 @@
 }
 
 #[test]
+fn alt_dynamic_array() {
+    fn alt1<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8]> {
+        alt(&mut ["a", "bc", "def"][..]).parse_next(i)
+    }
+
+    let a = &b"a"[..];
+    assert_eq!(alt1.parse_peek(a), Ok((&b""[..], (&b"a"[..]))));
+
+    let bc = &b"bc"[..];
+    assert_eq!(alt1.parse_peek(bc), Ok((&b""[..], (&b"bc"[..]))));
+
+    let defg = &b"defg"[..];
+    assert_eq!(alt1.parse_peek(defg), Ok((&b"g"[..], (&b"def"[..]))));
+}
+
+#[test]
 fn permutation_test() {
     #[allow(clippy::type_complexity)]
     fn perm(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8], &[u8])> {
@@ -781,7 +775,7 @@
     let i_err_pos = &i[3..];
     assert_eq!(
         empty_sep(Partial::new(i)),
-        Err(ErrMode::Backtrack(error_position!(
+        Err(ErrMode::Cut(error_position!(
             &Partial::new(i_err_pos),
             ErrorKind::Assert
         )))
@@ -922,7 +916,7 @@
 
     assert_eq!(
         multi_empty(Partial::new(&b"abcdef"[..])),
-        Err(ErrMode::Backtrack(error_position!(
+        Err(ErrMode::Cut(error_position!(
             &Partial::new(&b"abcdef"[..]),
             ErrorKind::Assert
         )))
@@ -1031,7 +1025,7 @@
 #[cfg(feature = "std")]
 fn infinite_many() {
     fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
-        println!("input: {:?}", input);
+        println!("input: {input:?}");
         Err(ErrMode::Backtrack(error_position!(&input, ErrorKind::Tag)))
     }
 
@@ -1176,7 +1170,7 @@
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct NilError;
+struct NilError;
 
 impl<I> From<(I, ErrorKind)> for NilError {
     fn from(_: (I, ErrorKind)) -> Self {
@@ -1184,11 +1178,11 @@
     }
 }
 
-impl<I> ParserError<I> for NilError {
+impl<I: Stream> ParserError<I> for NilError {
     fn from_error_kind(_: &I, _: ErrorKind) -> NilError {
         NilError
     }
-    fn append(self, _: &I, _: ErrorKind) -> NilError {
+    fn append(self, _: &I, _: &<I as Stream>::Checkpoint, _: ErrorKind) -> NilError {
         NilError
     }
 }
@@ -1246,7 +1240,7 @@
 
     assert_eq!(
         multi_empty(Partial::new(&b"abcdef"[..])),
-        Err(ErrMode::Backtrack(error_position!(
+        Err(ErrMode::Cut(error_position!(
             &Partial::new(&b"abcdef"[..]),
             ErrorKind::Assert
         )))
diff --git a/crates/winnow/src/error.rs b/crates/winnow/src/error.rs
index 3f28013..c70b1a5 100644
--- a/crates/winnow/src/error.rs
+++ b/crates/winnow/src/error.rs
@@ -30,6 +30,18 @@
 #[allow(unused_imports)] // Here for intra-doc links
 use crate::Parser;
 
+/// For use with [`Parser::parse_next`]
+///
+/// - `Ok(O)` is the parsed value
+/// - [`Err(ErrMode<E>)`][ErrMode] is the error along with how to respond to it
+///
+/// By default, the error type (`E`) is [`ContextError`].
+///
+/// When integrating into the result of the application, see
+/// - [`Parser::parse`]
+/// - [`ErrMode::into_inner`]
+pub type PResult<O, E = ContextError> = Result<O, ErrMode<E>>;
+
 /// For use with [`Parser::parse_peek`] which allows the input stream to be threaded through a
 /// parser.
 ///
@@ -43,28 +55,21 @@
 /// - [`ErrMode::into_inner`]
 pub type IResult<I, O, E = InputError<I>> = PResult<(I, O), E>;
 
-/// For use with [`Parser::parse_next`]
-///
-/// - `Ok(O)` is the parsed value
-/// - [`Err(ErrMode<E>)`][ErrMode] is the error along with how to respond to it
-///
-/// By default, the error type (`E`) is [`ContextError`].
-///
-/// When integrating into the result of the application, see
-/// - [`Parser::parse`]
-/// - [`ErrMode::into_inner`]
-pub type PResult<O, E = ContextError> = Result<O, ErrMode<E>>;
-
 /// Contains information on needed data if a parser returned `Incomplete`
 ///
+/// <div class="warning">
+///
 /// **Note:** This is only possible for `Stream` that are [partial][`crate::stream::StreamIsPartial`],
 /// like [`Partial`][crate::Partial].
+///
+/// </div>
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
 pub enum Needed {
     /// Needs more data, but we do not know how much
     Unknown,
-    /// Contains the required data size in bytes
+    /// Contains a lower bound on the buffer offset needed to finish parsing
+    ///
+    /// For byte/`&str` streams, this translates to bytes
     Size(NonZeroUsize),
 }
 
@@ -94,7 +99,6 @@
 
 /// Add parse error state to [`ParserError`]s
 #[derive(Debug, Clone, PartialEq)]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
 pub enum ErrMode<E> {
     /// There was not enough data to determine the appropriate action
     ///
@@ -168,7 +172,6 @@
     /// Unwrap the mode, returning the underlying error
     ///
     /// Returns `None` for [`ErrMode::Incomplete`]
-    #[cfg_attr(debug_assertions, track_caller)]
     #[inline(always)]
     pub fn into_inner(self) -> Option<E> {
         match self {
@@ -178,7 +181,7 @@
     }
 }
 
-impl<I, E: ParserError<I>> ParserError<I> for ErrMode<E> {
+impl<I: Stream, E: ParserError<I>> ParserError<I> for ErrMode<E> {
     #[inline(always)]
     fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
         ErrMode::Backtrack(E::from_error_kind(input, kind))
@@ -190,13 +193,13 @@
     where
         I: crate::lib::std::fmt::Debug,
     {
-        ErrMode::Backtrack(E::assert(input, message))
+        ErrMode::Cut(E::assert(input, message))
     }
 
     #[inline]
-    fn append(self, input: &I, kind: ErrorKind) -> Self {
+    fn append(self, input: &I, token_start: &<I as Stream>::Checkpoint, kind: ErrorKind) -> Self {
         match self {
-            ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, kind)),
+            ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, token_start, kind)),
             e => e,
         }
     }
@@ -220,10 +223,10 @@
     }
 }
 
-impl<I, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> {
+impl<I: Stream, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> {
     #[inline(always)]
-    fn add_context(self, input: &I, ctx: C) -> Self {
-        self.map(|err| err.add_context(input, ctx))
+    fn add_context(self, input: &I, token_start: &<I as Stream>::Checkpoint, context: C) -> Self {
+        self.map(|err| err.add_context(input, token_start, context))
     }
 }
 
@@ -255,10 +258,10 @@
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            ErrMode::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {} bytes/chars", u),
+            ErrMode::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {u} more data"),
             ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
-            ErrMode::Cut(c) => write!(f, "Parsing Failure: {:?}", c),
-            ErrMode::Backtrack(c) => write!(f, "Parsing Error: {:?}", c),
+            ErrMode::Cut(c) => write!(f, "Parsing Failure: {c:?}"),
+            ErrMode::Backtrack(c) => write!(f, "Parsing Error: {c:?}"),
         }
     }
 }
@@ -267,18 +270,19 @@
 ///
 /// It provides methods to create an error from some combinators,
 /// and combine existing errors in combinators like `alt`.
-pub trait ParserError<I>: Sized {
+pub trait ParserError<I: Stream>: Sized {
     /// Creates an error from the input position and an [`ErrorKind`]
     fn from_error_kind(input: &I, kind: ErrorKind) -> Self;
 
     /// Process a parser assertion
     #[cfg_attr(debug_assertions, track_caller)]
+    #[inline(always)]
     fn assert(input: &I, _message: &'static str) -> Self
     where
         I: crate::lib::std::fmt::Debug,
     {
         #[cfg(debug_assertions)]
-        panic!("assert `{}` failed at {:#?}", _message, input);
+        panic!("assert `{_message}` failed at {input:#?}");
         #[cfg(not(debug_assertions))]
         Self::from_error_kind(input, ErrorKind::Assert)
     }
@@ -287,7 +291,7 @@
     ///
     /// This is useful when backtracking through a parse tree, accumulating error context on the
     /// way.
-    fn append(self, input: &I, kind: ErrorKind) -> Self;
+    fn append(self, input: &I, token_start: &<I as Stream>::Checkpoint, kind: ErrorKind) -> Self;
 
     /// Combines errors from two different parse branches.
     ///
@@ -302,18 +306,25 @@
 /// Used by [`Parser::context`] to add custom data to error while backtracking
 ///
 /// May be implemented multiple times for different kinds of context.
-pub trait AddContext<I, C = &'static str>: Sized {
+pub trait AddContext<I: Stream, C = &'static str>: Sized {
     /// Append to an existing error custom data
     ///
     /// This is used mainly by [`Parser::context`], to add user friendly information
     /// to errors when backtracking through a parse tree
     #[inline]
-    fn add_context(self, _input: &I, _ctx: C) -> Self {
+    fn add_context(
+        self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        _context: C,
+    ) -> Self {
         self
     }
 }
 
 /// Capture context from when an error was recovered
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 pub trait FromRecoverableError<I: Stream, E> {
     /// Capture context from when an error was recovered
     fn from_recoverable_error(
@@ -343,8 +354,12 @@
 /// This is useful for testing of generic parsers to ensure the error happens at the right
 /// location.
 ///
+/// <div class="warning">
+///
 /// **Note:** [context][Parser::context] and inner errors (like from [`Parser::try_map`]) will be
 /// dropped.
+///
+/// </div>
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub struct InputError<I: Clone> {
     /// The input stream, pointing to the location where the error occurred
@@ -371,7 +386,7 @@
 }
 
 #[cfg(feature = "alloc")]
-impl<'i, I: ToOwned> InputError<&'i I>
+impl<I: ToOwned> InputError<&I>
 where
     <I as ToOwned>::Owned: Clone,
 {
@@ -381,7 +396,7 @@
     }
 }
 
-impl<I: Clone> ParserError<I> for InputError<I> {
+impl<I: Stream + Clone> ParserError<I> for InputError<I> {
     #[inline]
     fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
         Self {
@@ -391,13 +406,20 @@
     }
 
     #[inline]
-    fn append(self, _: &I, _: ErrorKind) -> Self {
+    fn append(
+        self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        _kind: ErrorKind,
+    ) -> Self {
         self
     }
 }
 
-impl<I: Clone, C> AddContext<I, C> for InputError<I> {}
+impl<I: Stream + Clone, C> AddContext<I, C> for InputError<I> {}
 
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I: Clone + Stream> FromRecoverableError<I, Self> for InputError<I> {
     #[inline]
     fn from_recoverable_error(
@@ -459,16 +481,24 @@
 {
 }
 
-impl<I> ParserError<I> for () {
+impl<I: Stream> ParserError<I> for () {
     #[inline]
     fn from_error_kind(_: &I, _: ErrorKind) -> Self {}
 
     #[inline]
-    fn append(self, _: &I, _: ErrorKind) -> Self {}
+    fn append(
+        self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        _kind: ErrorKind,
+    ) -> Self {
+    }
 }
 
-impl<I, C> AddContext<I, C> for () {}
+impl<I: Stream, C> AddContext<I, C> for () {}
 
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I: Stream> FromRecoverableError<I, Self> for () {
     #[inline]
     fn from_recoverable_error(
@@ -544,14 +574,19 @@
     }
 }
 
-impl<I, C> ParserError<I> for ContextError<C> {
+impl<I: Stream, C> ParserError<I> for ContextError<C> {
     #[inline]
     fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self {
         Self::new()
     }
 
     #[inline]
-    fn append(self, _input: &I, _kind: ErrorKind) -> Self {
+    fn append(
+        self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        _kind: ErrorKind,
+    ) -> Self {
         self
     }
 
@@ -561,15 +596,22 @@
     }
 }
 
-impl<C, I> AddContext<I, C> for ContextError<C> {
+impl<C, I: Stream> AddContext<I, C> for ContextError<C> {
     #[inline]
-    fn add_context(mut self, _input: &I, ctx: C) -> Self {
+    fn add_context(
+        mut self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        context: C,
+    ) -> Self {
         #[cfg(feature = "alloc")]
-        self.context.push(ctx);
+        self.context.push(context);
         self
     }
 }
 
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I: Stream, C> FromRecoverableError<I, Self> for ContextError<C> {
     #[inline]
     fn from_recoverable_error(
@@ -649,7 +691,7 @@
             if let Some(expression) = expression {
                 newline = true;
 
-                write!(f, "invalid {}", expression)?;
+                write!(f, "invalid {expression}")?;
             }
 
             if !expected.is_empty() {
@@ -663,7 +705,7 @@
                     if i != 0 {
                         write!(f, ", ")?;
                     }
-                    write!(f, "{}", expected)?;
+                    write!(f, "{expected}")?;
                 }
             }
             #[cfg(feature = "std")]
@@ -672,7 +714,7 @@
                     if newline {
                         writeln!(f)?;
                     }
-                    write!(f, "{}", cause)?;
+                    write!(f, "{cause}")?;
                 }
             }
         }
@@ -681,6 +723,13 @@
     }
 }
 
+impl<C> ErrorConvert<ContextError<C>> for ContextError<C> {
+    #[inline]
+    fn convert(self) -> ContextError<C> {
+        self
+    }
+}
+
 /// Additional parse context for [`ContextError`] added via [`Parser::context`]
 #[derive(Clone, Debug, PartialEq, Eq)]
 #[non_exhaustive]
@@ -734,9 +783,9 @@
             Self::CharLiteral(c) if c.is_ascii_control() => {
                 write!(f, "`{}`", c.escape_debug())
             }
-            Self::CharLiteral(c) => write!(f, "`{}`", c),
-            Self::StringLiteral(c) => write!(f, "`{}`", c),
-            Self::Description(c) => write!(f, "{}", c),
+            Self::CharLiteral(c) => write!(f, "`{c}`"),
+            Self::StringLiteral(c) => write!(f, "`{c}`"),
+            Self::Description(c) => write!(f, "{c}"),
         }
     }
 }
@@ -793,6 +842,7 @@
 #[cfg(feature = "std")]
 impl<'i, I: ToOwned, C> TreeError<&'i I, C>
 where
+    &'i I: Stream + Clone,
     <I as ToOwned>::Owned: Clone,
 {
     /// Obtaining ownership
@@ -804,7 +854,7 @@
 #[cfg(feature = "std")]
 impl<I, C> TreeError<I, C>
 where
-    I: Clone,
+    I: Stream + Clone,
 {
     /// Translate the input type
     pub fn map_input<I2: Clone, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> {
@@ -857,7 +907,7 @@
 #[cfg(feature = "std")]
 impl<I, C> ParserError<I> for TreeError<I, C>
 where
-    I: Clone,
+    I: Stream + Clone,
 {
     fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
         TreeError::Base(TreeErrorBase {
@@ -867,9 +917,11 @@
         })
     }
 
-    fn append(self, input: &I, kind: ErrorKind) -> Self {
+    fn append(self, input: &I, token_start: &<I as Stream>::Checkpoint, kind: ErrorKind) -> Self {
+        let mut input = input.clone();
+        input.reset(token_start);
         let frame = TreeErrorFrame::Kind(TreeErrorBase {
-            input: input.clone(),
+            input,
             kind,
             cause: None,
         });
@@ -898,19 +950,19 @@
 #[cfg(feature = "std")]
 impl<I, C> AddContext<I, C> for TreeError<I, C>
 where
-    I: Clone,
+    I: Stream + Clone,
 {
-    fn add_context(self, input: &I, context: C) -> Self {
-        let frame = TreeErrorFrame::Context(TreeErrorContext {
-            input: input.clone(),
-            context,
-        });
+    fn add_context(self, input: &I, token_start: &<I as Stream>::Checkpoint, context: C) -> Self {
+        let mut input = input.clone();
+        input.reset(token_start);
+        let frame = TreeErrorFrame::Context(TreeErrorContext { input, context });
         self.append_frame(frame)
     }
 }
 
 #[cfg(feature = "std")]
-impl<I: Clone + Stream, C> FromRecoverableError<I, Self> for TreeError<I, C> {
+#[cfg(feature = "unstable-recover")]
+impl<I: Stream + Clone, C> FromRecoverableError<I, Self> for TreeError<I, C> {
     #[inline]
     fn from_recoverable_error(
         _token_start: &<I as Stream>::Checkpoint,
@@ -925,7 +977,7 @@
 #[cfg(feature = "std")]
 impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C>
 where
-    I: Clone,
+    I: Stream + Clone,
 {
     fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self {
         TreeError::Base(TreeErrorBase {
@@ -939,7 +991,7 @@
 #[cfg(feature = "std")]
 impl<I, C> TreeError<I, C>
 where
-    I: Clone + crate::lib::std::fmt::Display,
+    I: Stream + Clone + crate::lib::std::fmt::Display,
     C: fmt::Display,
 {
     fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
@@ -974,7 +1026,7 @@
 }
 
 #[cfg(feature = "std")]
-impl<I: Clone + fmt::Display> fmt::Display for TreeErrorBase<I> {
+impl<I: Stream + Clone + fmt::Display> fmt::Display for TreeErrorBase<I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if let Some(cause) = self.cause.as_ref() {
             write!(f, "caused by {cause}")?;
@@ -989,7 +1041,7 @@
 }
 
 #[cfg(feature = "std")]
-impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> {
+impl<I: Stream + Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let context = &self.context;
         let input = abbreviate(self.input.to_string());
@@ -1000,7 +1052,7 @@
 
 #[cfg(feature = "std")]
 impl<
-        I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
+        I: Stream + Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
         C: fmt::Display + fmt::Debug,
     > std::error::Error for TreeError<I, C>
 {
@@ -1030,119 +1082,12 @@
 }
 
 #[cfg(feature = "std")]
-impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> {
+impl<I: Stream + Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.write(f, 0)
     }
 }
 
-/// Deprecated, replaced with [`ContextError`]
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")]
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct VerboseError<I: Clone, C = &'static str> {
-    /// Accumulated error information
-    pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind<C>)>,
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<'i, I: ToOwned, C> VerboseError<&'i I, C>
-where
-    <I as ToOwned>::Owned: Clone,
-{
-    /// Obtaining ownership
-    pub fn into_owned(self) -> VerboseError<<I as ToOwned>::Owned, C> {
-        self.map_input(ToOwned::to_owned)
-    }
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<I: Clone, C> VerboseError<I, C> {
-    /// Translate the input type
-    pub fn map_input<I2: Clone, O>(self, op: O) -> VerboseError<I2, C>
-    where
-        O: Fn(I) -> I2,
-    {
-        VerboseError {
-            errors: self.errors.into_iter().map(|(i, k)| (op(i), k)).collect(),
-        }
-    }
-}
-
-/// Deprecated, replaced with [`ContextError`]
-#[cfg(feature = "std")]
-#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")]
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum VerboseErrorKind<C = &'static str> {
-    /// Static string added by the `context` function
-    Context(C),
-    /// Error kind given by various parsers
-    Winnow(ErrorKind),
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<I: Clone, C> ParserError<I> for VerboseError<I, C> {
-    fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
-        VerboseError {
-            errors: vec![(input.clone(), VerboseErrorKind::Winnow(kind))],
-        }
-    }
-
-    fn append(mut self, input: &I, kind: ErrorKind) -> Self {
-        self.errors
-            .push((input.clone(), VerboseErrorKind::Winnow(kind)));
-        self
-    }
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<I: Clone, C> AddContext<I, C> for VerboseError<I, C> {
-    fn add_context(mut self, input: &I, ctx: C) -> Self {
-        self.errors
-            .push((input.clone(), VerboseErrorKind::Context(ctx)));
-        self
-    }
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<I: Clone, C, E> FromExternalError<I, E> for VerboseError<I, C> {
-    /// Create a new error from an input position and an external error
-    fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self {
-        Self::from_error_kind(input, kind)
-    }
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for VerboseError<I, C> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        writeln!(f, "Parse error:")?;
-        for (input, error) in &self.errors {
-            match error {
-                VerboseErrorKind::Winnow(e) => writeln!(f, "{} at: {}", e.description(), input)?,
-                VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
-            }
-        }
-
-        Ok(())
-    }
-}
-
-#[cfg(feature = "std")]
-#[allow(deprecated)]
-impl<
-        I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
-        C: fmt::Display + fmt::Debug,
-    > std::error::Error for VerboseError<I, C>
-{
-}
-
 /// Provide some minor debug context for errors
 #[rustfmt::skip]
 #[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)]
@@ -1181,19 +1126,24 @@
   }
 }
 
-impl<I> ParserError<I> for ErrorKind {
+impl<I: Stream> ParserError<I> for ErrorKind {
     #[inline]
     fn from_error_kind(_input: &I, kind: ErrorKind) -> Self {
         kind
     }
 
     #[inline]
-    fn append(self, _: &I, _: ErrorKind) -> Self {
+    fn append(
+        self,
+        _input: &I,
+        _token_start: &<I as Stream>::Checkpoint,
+        _kind: ErrorKind,
+    ) -> Self {
         self
     }
 }
 
-impl<I, C> AddContext<I, C> for ErrorKind {}
+impl<I: Stream, C> AddContext<I, C> for ErrorKind {}
 
 impl<I, E> FromExternalError<I, E> for ErrorKind {
     /// Create a new error from an input position and an external error
@@ -1206,7 +1156,7 @@
 /// The Display implementation allows the `std::error::Error` implementation
 impl fmt::Display for ErrorKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "error {:?}", self)
+        write!(f, "error {self:?}")
     }
 }
 
@@ -1224,7 +1174,7 @@
 impl<I: Stream, E: ParserError<I>> ParseError<I, E> {
     pub(crate) fn new(mut input: I, start: I::Checkpoint, inner: E) -> Self {
         let offset = input.offset_from(&start);
-        input.reset(start);
+        input.reset(&start);
         Self {
             input,
             offset,
@@ -1242,8 +1192,12 @@
 
     /// The location in [`ParseError::input`] where parsing failed
     ///
+    /// <div class="warning">
+    ///
     /// **Note:** This is an offset, not an index, and may point to the end of input
     /// (`input.len()`) on eof errors.
+    ///
+    /// </div>
     #[inline]
     pub fn offset(&self) -> usize {
         self.offset
@@ -1282,7 +1236,7 @@
                 .nth(line_idx)
                 .expect("valid line number");
 
-            writeln!(f, "parse error at line {}, column {}", line_num, col_num)?;
+            writeln!(f, "parse error at line {line_num}, column {col_num}")?;
             //   |
             for _ in 0..gutter {
                 write!(f, " ")?;
@@ -1290,7 +1244,7 @@
             writeln!(f, " |")?;
 
             // 1 | 00:32:00.a999999
-            write!(f, "{} | ", line_num)?;
+            write!(f, "{line_num} | ")?;
             writeln!(f, "{}", String::from_utf8_lossy(content))?;
 
             //   |          ^
@@ -1462,6 +1416,7 @@
 #[cfg(test)]
 macro_rules! error_node_position(
   ($input:expr, $code:expr, $next:expr) => ({
-    $crate::error::ParserError::append($next, $input, $code)
+    let start = $input.checkpoint();
+    $crate::error::ParserError::append($next, $input, &start, $code)
   });
 );
diff --git a/crates/winnow/src/lib.rs b/crates/winnow/src/lib.rs
index 6dcbda5..08acb6f 100644
--- a/crates/winnow/src/lib.rs
+++ b/crates/winnow/src/lib.rs
@@ -7,7 +7,7 @@
 //! - [Tutorial][_tutorial::chapter_0]
 //! - [Special Topics][_topic]
 //! - [Discussions](https://github.com/winnow-rs/winnow/discussions)
-//! - [CHANGELOG](https://github.com/winnow-rs/winnow/blob/v0.5.37/CHANGELOG.md) (includes major version migration
+//! - [CHANGELOG](https://github.com/winnow-rs/winnow/blob/v0.6.24/CHANGELOG.md) (includes major version migration
 //!   guides)
 //!
 //! ## Aspirations
@@ -52,95 +52,12 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 #![warn(missing_docs)]
 #![warn(clippy::std_instead_of_core)]
-// BEGIN - Embark standard lints v6 for Rust 1.55+
-// do not change or add/remove here, but one can add exceptions after this section
-// for more info see: <https://github.com/EmbarkStudios/rust-ecosystem/issues/59>
-// "-Dunsafe_code",
-#![warn(clippy::all)]
-#![warn(clippy::await_holding_lock)]
-#![warn(clippy::char_lit_as_u8)]
-#![warn(clippy::checked_conversions)]
-#![warn(clippy::dbg_macro)]
-#![warn(clippy::debug_assert_with_mut_call)]
-#![warn(clippy::doc_markdown)]
-#![warn(clippy::empty_enum)]
-#![warn(clippy::enum_glob_use)]
-#![warn(clippy::exit)]
-#![warn(clippy::expl_impl_clone_on_copy)]
-#![warn(clippy::explicit_deref_methods)]
-#![warn(clippy::explicit_into_iter_loop)]
-#![warn(clippy::fallible_impl_from)]
-#![warn(clippy::filter_map_next)]
-#![warn(clippy::flat_map_option)]
-#![warn(clippy::float_cmp_const)]
-#![warn(clippy::fn_params_excessive_bools)]
-#![warn(clippy::from_iter_instead_of_collect)]
-#![warn(clippy::if_let_mutex)]
-#![warn(clippy::implicit_clone)]
-#![warn(clippy::imprecise_flops)]
-#![warn(clippy::inefficient_to_string)]
-#![warn(clippy::invalid_upcast_comparisons)]
-#![warn(clippy::large_digit_groups)]
-#![warn(clippy::large_stack_arrays)]
-#![warn(clippy::large_types_passed_by_value)]
-#![warn(clippy::let_unit_value)]
-#![warn(clippy::linkedlist)]
-#![warn(clippy::lossy_float_literal)]
-#![warn(clippy::macro_use_imports)]
-#![warn(clippy::manual_ok_or)]
-#![warn(clippy::map_err_ignore)]
-#![warn(clippy::map_flatten)]
-#![warn(clippy::map_unwrap_or)]
-#![warn(clippy::match_on_vec_items)]
-#![warn(clippy::match_same_arms)]
-#![warn(clippy::match_wild_err_arm)]
-#![warn(clippy::match_wildcard_for_single_variants)]
-#![warn(clippy::mem_forget)]
-#![warn(clippy::mismatched_target_os)]
-#![warn(clippy::missing_enforced_import_renames)]
-#![warn(clippy::mut_mut)]
-#![warn(clippy::mutex_integer)]
-#![warn(clippy::needless_borrow)]
-#![warn(clippy::needless_continue)]
-#![warn(clippy::needless_for_each)]
-#![warn(clippy::option_option)]
-#![warn(clippy::path_buf_push_overwrite)]
-#![warn(clippy::ptr_as_ptr)]
-#![warn(clippy::rc_mutex)]
-#![warn(clippy::ref_option_ref)]
-#![warn(clippy::rest_pat_in_fully_bound_structs)]
-#![warn(clippy::same_functions_in_if_condition)]
-#![warn(clippy::semicolon_if_nothing_returned)]
-#![warn(clippy::single_match_else)]
-#![warn(clippy::string_add_assign)]
-#![warn(clippy::string_add)]
-#![warn(clippy::string_lit_as_bytes)]
-#![warn(clippy::string_to_string)]
-#![warn(clippy::todo)]
-#![warn(clippy::trait_duplication_in_bounds)]
-#![warn(clippy::unimplemented)]
-#![warn(clippy::unnested_or_patterns)]
-#![warn(clippy::unused_self)]
-#![warn(clippy::useless_transmute)]
-#![warn(clippy::verbose_file_reads)]
-#![warn(clippy::zero_sized_map_values)]
-#![warn(future_incompatible)]
-#![warn(nonstandard_style)]
-#![warn(rust_2018_idioms)]
-// END - Embark standard lints v6 for Rust 1.55+
-#![allow(clippy::branches_sharing_code)]
-#![allow(clippy::collapsible_else_if)]
-#![allow(clippy::if_same_then_else)]
-#![allow(clippy::bool_assert_comparison)]
-#![allow(clippy::let_and_return)]
-#![allow(clippy::assertions_on_constants)]
-#![allow(clippy::map_unwrap_or)]
-#![allow(clippy::single_match_else)]
-#![allow(clippy::single_match)]
-#![allow(clippy::unnested_or_patterns)]
-#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+#![warn(clippy::print_stderr)]
+#![warn(clippy::print_stdout)]
+
 #[cfg(feature = "alloc")]
 #[cfg_attr(test, macro_use)]
+#[allow(unused_extern_crates)]
 extern crate alloc;
 #[cfg(doctest)]
 extern crate doc_comment;
@@ -152,44 +69,36 @@
 /// it, albeit there it is not public.
 #[doc(hidden)]
 pub(crate) mod lib {
+    #![allow(unused_imports)]
+
     /// `std` facade allowing `std`/`core` to be interchangeable. Reexports `alloc` crate optionally,
     /// as well as `core` or `std`
     #[cfg(not(feature = "std"))]
     /// internal std exports for no_std compatibility
-    pub mod std {
+    pub(crate) mod std {
         #[doc(hidden)]
         #[cfg(not(feature = "alloc"))]
-        pub use core::borrow;
+        pub(crate) use core::borrow;
 
         #[cfg(feature = "alloc")]
         #[doc(hidden)]
-        pub use alloc::{borrow, boxed, collections, string, vec};
+        pub(crate) use alloc::{borrow, boxed, collections, string, vec};
 
         #[doc(hidden)]
-        pub use core::{cmp, convert, fmt, hash, iter, mem, ops, option, result, slice, str};
-
-        /// internal reproduction of std prelude
-        #[doc(hidden)]
-        pub mod prelude {
-            pub use core::prelude as v1;
-        }
+        pub(crate) use core::{
+            cmp, convert, fmt, hash, iter, mem, ops, option, result, slice, str,
+        };
     }
 
     #[cfg(feature = "std")]
     /// internal std exports for `no_std` compatibility
-    pub mod std {
+    pub(crate) mod std {
         #![allow(clippy::std_instead_of_core)]
         #[doc(hidden)]
-        pub use std::{
-            alloc, borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, ops, option,
-            result, slice, str, string, vec,
+        pub(crate) use std::{
+            borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, ops, result, slice,
+            str, string, vec,
         };
-
-        /// internal reproduction of std prelude
-        #[doc(hidden)]
-        pub mod prelude {
-            pub use std::prelude as v1;
-        }
     }
 }
 
@@ -207,7 +116,6 @@
 pub mod binary;
 pub mod combinator;
 pub mod token;
-pub mod trace;
 
 #[cfg(feature = "unstable-doc")]
 pub mod _topic;
@@ -241,6 +149,7 @@
     pub use crate::PResult;
     pub use crate::Parser;
     #[cfg(feature = "unstable-recover")]
+    #[cfg(feature = "std")]
     pub use crate::RecoverableParser as _;
 }
 
@@ -249,7 +158,9 @@
 pub use parser::*;
 pub use stream::BStr;
 pub use stream::Bytes;
+#[allow(deprecated)]
 pub use stream::Located;
+pub use stream::LocatingSlice;
 pub use stream::Partial;
 pub use stream::Stateful;
 pub use stream::Str;
diff --git a/crates/winnow/src/macros/mod.rs b/crates/winnow/src/macros/mod.rs
index 09dc68b..cedb897 100644
--- a/crates/winnow/src/macros/mod.rs
+++ b/crates/winnow/src/macros/mod.rs
@@ -2,4 +2,4 @@
 mod seq;
 
 #[cfg(test)]
-mod test;
+mod tests;
diff --git a/crates/winnow/src/macros/seq.rs b/crates/winnow/src/macros/seq.rs
index e104391..a9991c9 100644
--- a/crates/winnow/src/macros/seq.rs
+++ b/crates/winnow/src/macros/seq.rs
@@ -1,5 +1,13 @@
 /// Initialize a struct or tuple out of a sequences of parsers
 ///
+/// Unlike normal struct initialization syntax:
+/// - `_` fields can exist to run a parser but ignore the result
+/// - Parse results for a field can later be referenced using the field name
+///
+/// Unlike normal tuple initialization syntax:
+/// - Struct-style initialization (`{ 0: _, 1: _}`) is not supported
+/// - `_: <parser>` fields can exist to run a parser but ignore the result
+///
 ///# Example
 ///
 /// ```
@@ -62,23 +70,22 @@
 #[doc(alias = "pair")]
 #[doc(alias = "separated_pair")]
 #[doc(alias = "struct_parser")]
+#[doc(hidden)] // forced to be visible in intended location
 macro_rules! seq {
-    ($name: ident { $($fields: tt)* }) => {
-        $crate::combinator::trace(stringify!($name), move |input: &mut _| {
-            use $crate::Parser;
+    ($($name: ident)::* { $($fields: tt)* }) => {
+        $crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| {
             $crate::seq_parse_struct_fields!(input; $($fields)*);
             #[allow(clippy::redundant_field_names)]
-            Ok($crate::seq_init_struct_fields!( ($($fields)*); $name;))
+            Ok($crate::seq_init_struct_fields!( ($($fields)*); $($name)::*;))
         })
     };
-    ($name: ident ( $($elements: tt)* )) => {
-        $crate::combinator::trace(stringify!($name), move |input: &mut _| {
-            use $crate::Parser;
+    ($($name: ident)::* ( $($elements: tt)* )) => {
+        $crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| {
             $crate::seq_parse_tuple_fields!( ($($elements)*) ; ).map(|t| {
                 $crate::seq_init_tuple_fields!(
                     ($($elements)*);
                     (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, t.9, t.10, t.11, t.12, t.13, t.14, t.15, t.16, t.17, t.18, t.19, t.20);
-                    $name;
+                    $($name)::*;
                 )
             }).parse_next(input)
         })
@@ -107,27 +114,27 @@
         $input: ident;
         _ : $head_parser: expr, $($fields: tt)*
     ) => {
-        let _ = $head_parser.parse_next($input)?;
+        let _ = $crate::Parser::parse_next(&mut $head_parser, $input)?;
         $crate::seq_parse_struct_fields!($input; $($fields)*)
     };
     (
         $input: ident;
         _ : $head_parser: expr
     ) => {
-        let _ = $head_parser.parse_next($input)?;
+        let _ = $crate::Parser::parse_next(&mut $head_parser, $input)?;
     };
     (
         $input: ident;
         $head_field: ident : $head_parser: expr, $($fields: tt)*
     ) => {
-        let $head_field = $head_parser.parse_next($input)?;
+        let $head_field = $crate::Parser::parse_next(&mut $head_parser, $input)?;
         $crate::seq_parse_struct_fields!($input; $($fields)*)
     };
     (
         $input: ident;
         $head_field: ident : $head_parser: expr
     ) => {
-        let $head_field = $head_parser.parse_next($input)?;
+        let $head_field = $crate::Parser::parse_next(&mut $head_parser, $input)?;
     };
     (
         $input: expr;
@@ -179,46 +186,46 @@
 macro_rules! seq_init_struct_fields {
     (
         (_ : $head_parser: expr, $($fields: tt)*);
-        $name: ident;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_struct_fields!( ( $($fields)* ) ; $name ; $($inits)* )
+        $crate::seq_init_struct_fields!( ( $($fields)* ) ; $($name)::* ; $($inits)* )
     };
     (
         (_ : $head_parser: expr);
-        $name: ident;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_struct_fields!( (); $name ; $($inits)* )
+        $crate::seq_init_struct_fields!( (); $($name)::* ; $($inits)* )
     };
     (
         ($head_field: ident : $head_parser: expr, $($fields: tt)*);
-        $name: ident;
+        $($name: ident)::*;
         $($inits: tt)*
     ) =>
     {
-        $crate::seq_init_struct_fields!( ( $($fields)* ) ; $name ; $($inits)* $head_field: $head_field, )
+        $crate::seq_init_struct_fields!( ( $($fields)* ) ; $($name)::* ; $($inits)* $head_field: $head_field, )
     };
     (
         ($head_field: ident : $head_parser: expr);
-        $name: ident;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_struct_fields!( (); $name ; $($inits)* $head_field: $head_field,)
+        $crate::seq_init_struct_fields!( (); $($name)::* ; $($inits)* $head_field: $head_field,)
     };
     (
         (.. $update: expr);
-        $name: ident;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $name { $($inits)* ..$update }
+        $($name)::* { $($inits)* ..$update }
     };
     (
         ($(,)?);
-        $name: ident;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $name { $($inits)* }
+        $($name)::* { $($inits)* }
     };
 }
 
@@ -228,41 +235,41 @@
     (
         (_ : $head_parser: expr, $($fields: tt)*);
         ($head_arg: expr, $($args: expr),*);
-        $($name: ident)?;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_tuple_fields!( ( $($fields)* ); ( $($args),* ) ; $($name)? ; $($inits)* )
+        $crate::seq_init_tuple_fields!( ( $($fields)* ); ( $($args),* ) ; $($name)::* ; $($inits)* )
     };
     (
         (_ : $head_parser: expr);
         ($head_arg: expr, $($args: expr),*);
-        $($name: ident)?;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_tuple_fields!((); ( $($args),* ); $($name)? ; $($inits)*)
+        $crate::seq_init_tuple_fields!((); ( $($args),* ); $($name)::* ; $($inits)*)
     };
     (
         ($head_parser: expr, $($fields: tt)*);
         ($head_arg: expr, $($args: expr),*);
-        $($name: ident)?;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_tuple_fields!( ( $($fields)* ) ; ( $($args),* ) ; $($name)? ; $($inits)* $head_arg, )
+        $crate::seq_init_tuple_fields!( ( $($fields)* ) ; ( $($args),* ) ; $($name)::* ; $($inits)* $head_arg, )
     };
     (
         ($head_parser: expr);
         ($head_arg: expr, $($args: expr),*);
-        $($name: ident)?;
+        $($name: ident)::*;
         $($inits: tt)*
     ) => {
-        $crate::seq_init_tuple_fields!((); ( $($args),* ); $($name)? ; $($inits)* $head_arg)
+        $crate::seq_init_tuple_fields!((); ( $($args),* ); $($name)::* ; $($inits)* $head_arg)
     };
     (
         ();
         ($($args: expr),*);
-        $($name: ident)?;
+        $($name: ident)::*;
         $($inits: expr),* $(,)?
     ) => {
-        $($name)?( $($inits,)* )
+        $($name)::*( $($inits,)* )
     };
 }
diff --git a/crates/winnow/src/macros/test.rs b/crates/winnow/src/macros/tests.rs
similarity index 90%
rename from crates/winnow/src/macros/test.rs
rename to crates/winnow/src/macros/tests.rs
index 17601bc..ef50133 100644
--- a/crates/winnow/src/macros/test.rs
+++ b/crates/winnow/src/macros/tests.rs
@@ -188,6 +188,43 @@
 }
 
 #[test]
+fn seq_enum_struct_variant() {
+    #[derive(Debug, PartialEq, Eq)]
+    enum Expr {
+        Add { lhs: u32, rhs: u32 },
+        Mul(u32, u32),
+    }
+
+    fn add(input: &mut &[u8]) -> PResult<Expr> {
+        seq! {Expr::Add {
+            lhs: dec_uint::<_, u32, _>,
+            _: b" + ",
+            rhs: dec_uint::<_, u32, _>,
+        }}
+        .parse_next(input)
+    }
+
+    fn mul(input: &mut &[u8]) -> PResult<Expr> {
+        seq!(Expr::Mul(
+             dec_uint::<_, u32, _>,
+             _: b" * ",
+             dec_uint::<_, u32, _>,
+        ))
+        .parse_next(input)
+    }
+
+    assert_eq!(
+        add.parse_peek(&b"1 + 2"[..]),
+        Ok((&b""[..], Expr::Add { lhs: 1, rhs: 2 })),
+    );
+
+    assert_eq!(
+        mul.parse_peek(&b"3 * 4"[..]),
+        Ok((&b""[..], Expr::Mul(3, 4))),
+    );
+}
+
+#[test]
 fn seq_tuple_struct_basics() {
     #[derive(Debug, PartialEq)]
     struct Point(u32, u32);
diff --git a/crates/winnow/src/parser.rs b/crates/winnow/src/parser.rs
index 1afa7e5..7d49c63 100644
--- a/crates/winnow/src/parser.rs
+++ b/crates/winnow/src/parser.rs
@@ -1,12 +1,14 @@
 //! Basic types to build the parsers
 
 use crate::ascii::Caseless as AsciiCaseless;
-use crate::combinator::*;
+use crate::combinator::impls;
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 use crate::error::FromRecoverableError;
 use crate::error::{AddContext, FromExternalError, IResult, PResult, ParseError, ParserError};
-use crate::stream::{AsChar, Compare, Location, ParseSlice, Stream, StreamIsPartial};
+use crate::stream::{Compare, Location, ParseSlice, Stream, StreamIsPartial};
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 use crate::stream::{Recover, Recoverable};
 
 /// Core trait for parsing
@@ -42,7 +44,7 @@
 ///
 /// Additionally, some basic types implement `Parser` as well, including
 /// - `u8` and `char`, see [`winnow::token::one_of`][crate::token::one_of]
-/// - `&[u8]` and `&str`, see [`winnow::token::tag`][crate::token::tag]
+/// - `&[u8]` and `&str`, see [`winnow::token::literal`][crate::token::literal]
 pub trait Parser<I, O, E> {
     /// Parse all of `input`, generating `O` from it
     #[inline]
@@ -52,7 +54,6 @@
         I: Stream,
         // Force users to deal with `Incomplete` when `StreamIsPartial<true>`
         I: StreamIsPartial,
-        I: Clone,
         E: ParserError<I>,
     {
         debug_assert!(
@@ -81,7 +82,18 @@
 
     /// Take tokens from the [`Stream`], turning it into the output
     ///
-    /// This includes advancing the [`Stream`] to the next location.
+    /// This returns a copy of the [`Stream`] advanced to the next location.
+    ///
+    /// <div class="warning">
+    ///
+    /// Generally, prefer [`Parser::parse_next`].
+    /// This is primarily intended for:
+    /// - Migrating from older versions / `nom`
+    /// - Testing [`Parser`]s
+    ///
+    /// For look-ahead parsing, see instead [`peek`][crate::combinator::peek].
+    ///
+    /// </div>
     #[inline(always)]
     fn parse_peek(&mut self, mut input: I) -> IResult<I, O, E> {
         match self.parse_next(&mut input) {
@@ -97,19 +109,19 @@
     /// # Example
     ///
     /// Because parsers are `FnMut`, they can be called multiple times. This prevents moving `f`
-    /// into [`length_data`][crate::binary::length_data] and `g` into
+    /// into [`length_take`][crate::binary::length_take] and `g` into
     /// [`Parser::complete_err`]:
     /// ```rust,compile_fail
     /// # use winnow::prelude::*;
     /// # use winnow::Parser;
     /// # use winnow::error::ParserError;
-    /// # use winnow::binary::length_data;
+    /// # use winnow::binary::length_take;
     /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>(
     ///     mut f: impl Parser<&'i [u8], usize, E>,
     ///     mut g: impl Parser<&'i [u8], O, E>
     /// ) -> impl Parser<&'i [u8], O, E> {
     ///   move |i: &mut &'i [u8]| {
-    ///     let mut data = length_data(f).parse_next(i)?;
+    ///     let mut data = length_take(f).parse_next(i)?;
     ///     let o = g.complete_err().parse_next(&mut data)?;
     ///     Ok(o)
     ///   }
@@ -121,24 +133,24 @@
     /// # use winnow::prelude::*;
     /// # use winnow::Parser;
     /// # use winnow::error::ParserError;
-    /// # use winnow::binary::length_data;
+    /// # use winnow::binary::length_take;
     /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>(
     ///     mut f: impl Parser<&'i [u8], usize, E>,
     ///     mut g: impl Parser<&'i [u8], O, E>
     /// ) -> impl Parser<&'i [u8], O, E> {
     ///   move |i: &mut &'i [u8]| {
-    ///     let mut data = length_data(f.by_ref()).parse_next(i)?;
+    ///     let mut data = length_take(f.by_ref()).parse_next(i)?;
     ///     let o = g.by_ref().complete_err().parse_next(&mut data)?;
     ///     Ok(o)
     ///   }
     /// }
     /// ```
     #[inline(always)]
-    fn by_ref(&mut self) -> ByRef<'_, Self>
+    fn by_ref(&mut self) -> impls::ByRef<'_, Self>
     where
         Self: core::marker::Sized,
     {
-        ByRef::new(self)
+        impls::ByRef { p: self }
     }
 
     /// Produce the provided value
@@ -158,12 +170,18 @@
     /// ```
     #[doc(alias = "to")]
     #[inline(always)]
-    fn value<O2>(self, val: O2) -> Value<Self, I, O, O2, E>
+    fn value<O2>(self, val: O2) -> impls::Value<Self, I, O, O2, E>
     where
         Self: core::marker::Sized,
         O2: Clone,
     {
-        Value::new(self, val)
+        impls::Value {
+            parser: self,
+            val,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Produce a type's default value
@@ -182,12 +200,18 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn default_value<O2>(self) -> DefaultValue<Self, I, O, O2, E>
+    fn default_value<O2>(self) -> impls::DefaultValue<Self, I, O, O2, E>
     where
         Self: core::marker::Sized,
         O2: core::default::Default,
     {
-        DefaultValue::new(self)
+        impls::DefaultValue {
+            parser: self,
+            o2: Default::default(),
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Discards the output of the `Parser`
@@ -206,11 +230,16 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn void(self) -> Void<Self, I, O, E>
+    fn void(self) -> impls::Void<Self, I, O, E>
     where
         Self: core::marker::Sized,
     {
-        Void::new(self)
+        impls::Void {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Convert the parser's output to another type using [`std::convert::From`]
@@ -235,12 +264,18 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn output_into<O2>(self) -> OutputInto<Self, I, O, O2, E>
+    fn output_into<O2>(self) -> impls::OutputInto<Self, I, O, O2, E>
     where
         Self: core::marker::Sized,
         O: Into<O2>,
     {
-        OutputInto::new(self)
+        impls::OutputInto {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Produce the consumed input as produced value.
@@ -253,25 +288,47 @@
     /// use winnow::combinator::separated_pair;
     /// # fn main() {
     ///
-    /// let mut parser = separated_pair(alpha1, ',', alpha1).recognize();
+    /// let mut parser = separated_pair(alpha1, ',', alpha1).take();
     ///
     /// assert_eq!(parser.parse_peek("abcd,efgh"), Ok(("", "abcd,efgh")));
-    /// assert_eq!(parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify))));
+    /// assert_eq!(parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Tag))));
     /// # }
     /// ```
     #[doc(alias = "concat")]
+    #[doc(alias = "recognize")]
     #[inline(always)]
-    fn recognize(self) -> Recognize<Self, I, O, E>
+    fn take(self) -> impls::Take<Self, I, O, E>
     where
         Self: core::marker::Sized,
         I: Stream,
     {
-        Recognize::new(self)
+        impls::Take {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
+    }
+
+    /// Replaced with [`Parser::take`]
+    #[inline(always)]
+    #[deprecated(since = "0.6.14", note = "Replaced with `Parser::take`")]
+    fn recognize(self) -> impls::Take<Self, I, O, E>
+    where
+        Self: core::marker::Sized,
+        I: Stream,
+    {
+        impls::Take {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Produce the consumed input with the output
     ///
-    /// Functions similarly to [recognize][Parser::recognize] except it
+    /// Functions similarly to [take][Parser::take] except it
     /// returns the parser output as well.
     ///
     /// This can be useful especially in cases where the output is not the same type
@@ -285,34 +342,56 @@
     /// # use winnow::prelude::*;
     /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError};
     /// use winnow::ascii::{alpha1};
-    /// use winnow::token::tag;
+    /// use winnow::token::literal;
     /// use winnow::combinator::separated_pair;
     ///
     /// fn inner_parser<'s>(input: &mut &'s str) -> PResult<bool, InputError<&'s str>> {
     ///     "1234".value(true).parse_next(input)
     /// }
     ///
-    /// let mut consumed_parser = separated_pair(alpha1, ',', alpha1).value(true).with_recognized();
+    /// let mut consumed_parser = separated_pair(alpha1, ',', alpha1).value(true).with_taken();
     ///
     /// assert_eq!(consumed_parser.parse_peek("abcd,efgh1"), Ok(("1", (true, "abcd,efgh"))));
-    /// assert_eq!(consumed_parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify))));
+    /// assert_eq!(consumed_parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Tag))));
     ///
     /// // the second output (representing the consumed input)
-    /// // should be the same as that of the `recognize` parser.
-    /// let mut recognize_parser = inner_parser.recognize();
-    /// let mut consumed_parser = inner_parser.with_recognized().map(|(output, consumed)| consumed);
+    /// // should be the same as that of the `take` parser.
+    /// let mut take_parser = inner_parser.take();
+    /// let mut consumed_parser = inner_parser.with_taken().map(|(output, consumed)| consumed);
     ///
-    /// assert_eq!(recognize_parser.parse_peek("1234"), consumed_parser.parse_peek("1234"));
-    /// assert_eq!(recognize_parser.parse_peek("abcd"), consumed_parser.parse_peek("abcd"));
+    /// assert_eq!(take_parser.parse_peek("1234"), consumed_parser.parse_peek("1234"));
+    /// assert_eq!(take_parser.parse_peek("abcd"), consumed_parser.parse_peek("abcd"));
     /// ```
     #[doc(alias = "consumed")]
+    #[doc(alias = "with_recognized")]
     #[inline(always)]
-    fn with_recognized(self) -> WithRecognized<Self, I, O, E>
+    fn with_taken(self) -> impls::WithTaken<Self, I, O, E>
     where
         Self: core::marker::Sized,
         I: Stream,
     {
-        WithRecognized::new(self)
+        impls::WithTaken {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
+    }
+
+    /// Replaced with [`Parser::with_taken`]
+    #[inline(always)]
+    #[deprecated(since = "0.6.14", note = "Replaced with `Parser::with_taken`")]
+    fn with_recognized(self) -> impls::WithTaken<Self, I, O, E>
+    where
+        Self: core::marker::Sized,
+        I: Stream,
+    {
+        impls::WithTaken {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Produce the location of the consumed input as produced value.
@@ -322,22 +401,27 @@
     /// ```rust
     /// # use winnow::prelude::*;
     /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream};
-    /// use winnow::stream::Located;
+    /// use winnow::stream::LocatingSlice;
     /// use winnow::ascii::alpha1;
     /// use winnow::combinator::separated_pair;
     ///
     /// let mut parser = separated_pair(alpha1.span(), ',', alpha1.span());
     ///
-    /// assert_eq!(parser.parse(Located::new("abcd,efgh")), Ok((0..4, 5..9)));
-    /// assert_eq!(parser.parse_peek(Located::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(Located::new("abcd;").peek_slice(4).0, ErrorKind::Verify))));
+    /// assert_eq!(parser.parse(LocatingSlice::new("abcd,efgh")), Ok((0..4, 5..9)));
+    /// assert_eq!(parser.parse_peek(LocatingSlice::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(LocatingSlice::new("abcd;").peek_slice(4).0, ErrorKind::Tag))));
     /// ```
     #[inline(always)]
-    fn span(self) -> Span<Self, I, O, E>
+    fn span(self) -> impls::Span<Self, I, O, E>
     where
         Self: core::marker::Sized,
         I: Stream + Location,
     {
-        Span::new(self)
+        impls::Span {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Produce the location of consumed input with the output
@@ -355,12 +439,12 @@
     /// ```rust
     /// # use winnow::prelude::*;
     /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream};
-    /// use winnow::stream::Located;
+    /// use winnow::stream::LocatingSlice;
     /// use winnow::ascii::alpha1;
-    /// use winnow::token::tag;
+    /// use winnow::token::literal;
     /// use winnow::combinator::separated_pair;
     ///
-    /// fn inner_parser<'s>(input: &mut Located<&'s str>) -> PResult<bool, InputError<Located<&'s str>>> {
+    /// fn inner_parser<'s>(input: &mut LocatingSlice<&'s str>) -> PResult<bool, InputError<LocatingSlice<&'s str>>> {
     ///     "1234".value(true).parse_next(input)
     /// }
     ///
@@ -368,25 +452,30 @@
     ///
     /// let mut consumed_parser = separated_pair(alpha1.value(1).with_span(), ',', alpha1.value(2).with_span());
     ///
-    /// assert_eq!(consumed_parser.parse(Located::new("abcd,efgh")), Ok(((1, 0..4), (2, 5..9))));
-    /// assert_eq!(consumed_parser.parse_peek(Located::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(Located::new("abcd;").peek_slice(4).0, ErrorKind::Verify))));
+    /// assert_eq!(consumed_parser.parse(LocatingSlice::new("abcd,efgh")), Ok(((1, 0..4), (2, 5..9))));
+    /// assert_eq!(consumed_parser.parse_peek(LocatingSlice::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(LocatingSlice::new("abcd;").peek_slice(4).0, ErrorKind::Tag))));
     ///
     /// // the second output (representing the consumed input)
     /// // should be the same as that of the `span` parser.
-    /// let mut recognize_parser = inner_parser.span();
+    /// let mut span_parser = inner_parser.span();
     /// let mut consumed_parser = inner_parser.with_span().map(|(output, consumed)| consumed);
     ///
-    /// assert_eq!(recognize_parser.parse_peek(Located::new("1234")), consumed_parser.parse_peek(Located::new("1234")));
-    /// assert_eq!(recognize_parser.parse_peek(Located::new("abcd")), consumed_parser.parse_peek(Located::new("abcd")));
+    /// assert_eq!(span_parser.parse_peek(LocatingSlice::new("1234")), consumed_parser.parse_peek(LocatingSlice::new("1234")));
+    /// assert_eq!(span_parser.parse_peek(LocatingSlice::new("abcd")), consumed_parser.parse_peek(LocatingSlice::new("abcd")));
     /// # }
     /// ```
     #[inline(always)]
-    fn with_span(self) -> WithSpan<Self, I, O, E>
+    fn with_span(self) -> impls::WithSpan<Self, I, O, E>
     where
         Self: core::marker::Sized,
         I: Stream + Location,
     {
-        WithSpan::new(self)
+        impls::WithSpan {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Maps a function over the output of a parser
@@ -408,12 +497,19 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn map<G, O2>(self, map: G) -> Map<Self, G, I, O, O2, E>
+    fn map<G, O2>(self, map: G) -> impls::Map<Self, G, I, O, O2, E>
     where
         G: FnMut(O) -> O2,
         Self: core::marker::Sized,
     {
-        Map::new(self, map)
+        impls::Map {
+            parser: self,
+            map,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Applies a function returning a `Result` over the output of a parser.
@@ -438,14 +534,22 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn try_map<G, O2, E2>(self, map: G) -> TryMap<Self, G, I, O, O2, E, E2>
+    fn try_map<G, O2, E2>(self, map: G) -> impls::TryMap<Self, G, I, O, O2, E, E2>
     where
         Self: core::marker::Sized,
         G: FnMut(O) -> Result<O2, E2>,
         I: Stream,
         E: FromExternalError<I, E2>,
     {
-        TryMap::new(self, map)
+        impls::TryMap {
+            parser: self,
+            map,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+            e2: Default::default(),
+        }
     }
 
     /// Apply both [`Parser::verify`] and [`Parser::map`].
@@ -473,14 +577,21 @@
     #[doc(alias = "filter_map")]
     #[doc(alias = "map_opt")]
     #[inline(always)]
-    fn verify_map<G, O2>(self, map: G) -> VerifyMap<Self, G, I, O, O2, E>
+    fn verify_map<G, O2>(self, map: G) -> impls::VerifyMap<Self, G, I, O, O2, E>
     where
         Self: core::marker::Sized,
         G: FnMut(O) -> Option<O2>,
         I: Stream,
         E: ParserError<I>,
     {
-        VerifyMap::new(self, map)
+        impls::VerifyMap {
+            parser: self,
+            map,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Creates a parser from the output of this one
@@ -492,12 +603,12 @@
     /// use winnow::token::take;
     /// use winnow::binary::u8;
     ///
-    /// fn length_data<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+    /// fn length_take<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
     ///     u8.flat_map(take).parse_next(input)
     /// }
     ///
-    /// assert_eq!(length_data.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..])));
-    /// assert_eq!(length_data.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice))));
+    /// assert_eq!(length_take.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..])));
+    /// assert_eq!(length_take.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice))));
     /// ```
     ///
     /// which is the same as
@@ -506,23 +617,31 @@
     /// use winnow::token::take;
     /// use winnow::binary::u8;
     ///
-    /// fn length_data<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+    /// fn length_take<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
     ///     let length = u8.parse_next(input)?;
     ///     let data = take(length).parse_next(input)?;
     ///     Ok(data)
     /// }
     ///
-    /// assert_eq!(length_data.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..])));
-    /// assert_eq!(length_data.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice))));
+    /// assert_eq!(length_take.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..])));
+    /// assert_eq!(length_take.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice))));
     /// ```
     #[inline(always)]
-    fn flat_map<G, H, O2>(self, map: G) -> FlatMap<Self, G, H, I, O, O2, E>
+    fn flat_map<G, H, O2>(self, map: G) -> impls::FlatMap<Self, G, H, I, O, O2, E>
     where
         Self: core::marker::Sized,
         G: FnMut(O) -> H,
         H: Parser<I, O2, E>,
     {
-        FlatMap::new(self, map)
+        impls::FlatMap {
+            f: self,
+            g: map,
+            h: Default::default(),
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Applies a second parser over the output of the first one
@@ -543,14 +662,21 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn and_then<G, O2>(self, inner: G) -> AndThen<Self, G, I, O, O2, E>
+    fn and_then<G, O2>(self, inner: G) -> impls::AndThen<Self, G, I, O, O2, E>
     where
         Self: core::marker::Sized,
         G: Parser<O, O2, E>,
         O: StreamIsPartial,
         I: Stream,
     {
-        AndThen::new(self, inner)
+        impls::AndThen {
+            outer: self,
+            inner,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Apply [`std::str::FromStr`] to the output of the parser
@@ -574,14 +700,20 @@
     /// ```
     #[doc(alias = "from_str")]
     #[inline(always)]
-    fn parse_to<O2>(self) -> ParseTo<Self, I, O, O2, E>
+    fn parse_to<O2>(self) -> impls::ParseTo<Self, I, O, O2, E>
     where
         Self: core::marker::Sized,
         I: Stream,
         O: ParseSlice<O2>,
         E: ParserError<I>,
     {
-        ParseTo::new(self)
+        impls::ParseTo {
+            p: self,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Returns the output of the child parser if it satisfies a verification function.
@@ -606,7 +738,7 @@
     #[doc(alias = "satisfy")]
     #[doc(alias = "filter")]
     #[inline(always)]
-    fn verify<G, O2>(self, filter: G) -> Verify<Self, G, I, O, O2, E>
+    fn verify<G, O2>(self, filter: G) -> impls::Verify<Self, G, I, O, O2, E>
     where
         Self: core::marker::Sized,
         G: FnMut(&O2) -> bool,
@@ -615,7 +747,14 @@
         O2: ?Sized,
         E: ParserError<I>,
     {
-        Verify::new(self, filter)
+        impls::Verify {
+            parser: self,
+            filter,
+            i: Default::default(),
+            o: Default::default(),
+            o2: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// If parsing fails, add context to the error
@@ -624,14 +763,20 @@
     /// to errors when backtracking through a parse tree.
     #[doc(alias = "labelled")]
     #[inline(always)]
-    fn context<C>(self, context: C) -> Context<Self, I, O, E, C>
+    fn context<C>(self, context: C) -> impls::Context<Self, I, O, E, C>
     where
         Self: core::marker::Sized,
         I: Stream,
         E: AddContext<I, C>,
         C: Clone + crate::lib::std::fmt::Debug,
     {
-        Context::new(self, context)
+        impls::Context {
+            parser: self,
+            context,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Transforms [`Incomplete`][crate::error::ErrMode::Incomplete] into [`Backtrack`][crate::error::ErrMode::Backtrack]
@@ -650,21 +795,27 @@
     /// # }
     /// ```
     #[inline(always)]
-    fn complete_err(self) -> CompleteErr<Self>
+    fn complete_err(self) -> impls::CompleteErr<Self>
     where
         Self: core::marker::Sized,
     {
-        CompleteErr::new(self)
+        impls::CompleteErr { f: self }
     }
 
     /// Convert the parser's error to another type using [`std::convert::From`]
     #[inline(always)]
-    fn err_into<E2>(self) -> ErrInto<Self, I, O, E, E2>
+    fn err_into<E2>(self) -> impls::ErrInto<Self, I, O, E, E2>
     where
         Self: core::marker::Sized,
         E: Into<E2>,
     {
-        ErrInto::new(self)
+        impls::ErrInto {
+            parser: self,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+            e2: Default::default(),
+        }
     }
 
     /// Recover from an error by skipping everything `recover` consumes and trying again
@@ -676,7 +827,8 @@
     /// [`winnow::combinator::alt`][crate::combinator::alt].
     #[inline(always)]
     #[cfg(feature = "unstable-recover")]
-    fn retry_after<R>(self, recover: R) -> RetryAfter<Self, R, I, O, E>
+    #[cfg(feature = "std")]
+    fn retry_after<R>(self, recover: R) -> impls::RetryAfter<Self, R, I, O, E>
     where
         Self: core::marker::Sized,
         R: Parser<I, (), E>,
@@ -684,7 +836,13 @@
         I: Recover<E>,
         E: FromRecoverableError<I, E>,
     {
-        RetryAfter::new(self, recover)
+        impls::RetryAfter {
+            parser: self,
+            recover,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 
     /// Recover from an error by skipping this parse and everything `recover` consumes
@@ -693,7 +851,8 @@
     /// [`winnow::combinator::alt`][crate::combinator::alt].
     #[inline(always)]
     #[cfg(feature = "unstable-recover")]
-    fn resume_after<R>(self, recover: R) -> ResumeAfter<Self, R, I, O, E>
+    #[cfg(feature = "std")]
+    fn resume_after<R>(self, recover: R) -> impls::ResumeAfter<Self, R, I, O, E>
     where
         Self: core::marker::Sized,
         R: Parser<I, (), E>,
@@ -701,13 +860,19 @@
         I: Recover<E>,
         E: FromRecoverableError<I, E>,
     {
-        ResumeAfter::new(self, recover)
+        impls::ResumeAfter {
+            parser: self,
+            recover,
+            i: Default::default(),
+            o: Default::default(),
+            e: Default::default(),
+        }
     }
 }
 
-impl<'a, I, O, E, F> Parser<I, O, E> for F
+impl<I, O, E, F> Parser<I, O, E> for F
 where
-    F: FnMut(&mut I) -> PResult<O, E> + 'a,
+    F: FnMut(&mut I) -> PResult<O, E>,
     I: Stream,
 {
     #[inline(always)]
@@ -727,19 +892,20 @@
 ///     b'a'.parse_next(i)
 /// }
 /// assert_eq!(parser.parse_peek(&b"abc"[..]), Ok((&b"bc"[..], b'a')));
-/// assert_eq!(parser.parse_peek(&b" abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b" abc"[..], ErrorKind::Verify))));
-/// assert_eq!(parser.parse_peek(&b"bc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"bc"[..], ErrorKind::Verify))));
-/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Token))));
+/// assert_eq!(parser.parse_peek(&b" abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b" abc"[..], ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(&b"bc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"bc"[..], ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Tag))));
 /// ```
 impl<I, E> Parser<I, u8, E> for u8
 where
     I: StreamIsPartial,
-    I: Stream<Token = u8>,
+    I: Stream,
+    I: Compare<u8>,
     E: ParserError<I>,
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<u8, E> {
-        crate::token::one_of(*self).parse_next(i)
+        crate::token::literal(*self).value(*self).parse_next(i)
     }
 }
 
@@ -754,24 +920,24 @@
 ///     'a'.parse_next(i)
 /// }
 /// assert_eq!(parser.parse_peek("abc"), Ok(("bc", 'a')));
-/// assert_eq!(parser.parse_peek(" abc"), Err(ErrMode::Backtrack(InputError::new(" abc", ErrorKind::Verify))));
-/// assert_eq!(parser.parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Verify))));
-/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// assert_eq!(parser.parse_peek(" abc"), Err(ErrMode::Backtrack(InputError::new(" abc", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
 /// ```
-impl<I, E> Parser<I, <I as Stream>::Token, E> for char
+impl<I, E> Parser<I, char, E> for char
 where
     I: StreamIsPartial,
     I: Stream,
-    <I as Stream>::Token: AsChar + Clone,
+    I: Compare<char>,
     E: ParserError<I>,
 {
     #[inline(always)]
-    fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Token, E> {
-        crate::token::one_of(*self).parse_next(i)
+    fn parse_next(&mut self, i: &mut I) -> PResult<char, E> {
+        crate::token::literal(*self).value(*self).parse_next(i)
     }
 }
 
-/// This is a shortcut for [`tag`][crate::token::tag].
+/// This is a shortcut for [`literal`][crate::token::literal].
 ///
 /// # Example
 /// ```rust
@@ -796,11 +962,11 @@
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        crate::token::tag(*self).parse_next(i)
+        crate::token::literal(*self).parse_next(i)
     }
 }
 
-/// This is a shortcut for [`tag`][crate::token::tag].
+/// This is a shortcut for [`literal`][crate::token::literal].
 ///
 /// # Example
 /// ```rust
@@ -828,11 +994,11 @@
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        crate::token::tag(*self).parse_next(i)
+        crate::token::literal(*self).parse_next(i)
     }
 }
 
-/// This is a shortcut for [`tag`][crate::token::tag].
+/// This is a shortcut for [`literal`][crate::token::literal].
 ///
 /// # Example
 /// ```rust
@@ -857,11 +1023,11 @@
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        crate::token::tag(*self).parse_next(i)
+        crate::token::literal(*self).parse_next(i)
     }
 }
 
-/// This is a shortcut for [`tag`][crate::token::tag].
+/// This is a shortcut for [`literal`][crate::token::literal].
 ///
 /// # Example
 /// ```rust
@@ -890,11 +1056,11 @@
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        crate::token::tag(*self).parse_next(i)
+        crate::token::literal(*self).parse_next(i)
     }
 }
 
-/// This is a shortcut for [`tag`][crate::token::tag].
+/// This is a shortcut for [`literal`][crate::token::literal].
 ///
 /// # Example
 /// ```rust
@@ -919,11 +1085,11 @@
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        crate::token::tag(*self).parse_next(i)
+        crate::token::literal(*self).parse_next(i)
     }
 }
 
-/// This is a shortcut for [`tag`][crate::token::tag].
+/// This is a shortcut for [`literal`][crate::token::literal].
 ///
 /// # Example
 /// ```rust
@@ -951,11 +1117,11 @@
 {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
-        crate::token::tag(*self).parse_next(i)
+        crate::token::literal(*self).parse_next(i)
     }
 }
 
-impl<I, E: ParserError<I>> Parser<I, (), E> for () {
+impl<I: Stream, E: ParserError<I>> Parser<I, (), E> for () {
     #[inline(always)]
     fn parse_next(&mut self, _i: &mut I) -> PResult<(), E> {
         Ok(())
@@ -965,7 +1131,7 @@
 macro_rules! impl_parser_for_tuple {
   ($($parser:ident $output:ident),+) => (
     #[allow(non_snake_case)]
-    impl<I, $($output),+, E: ParserError<I>, $($parser),+> Parser<I, ($($output),+,), E> for ($($parser),+,)
+    impl<I: Stream, $($output),+, E: ParserError<I>, $($parser),+> Parser<I, ($($output),+,), E> for ($($parser),+,)
     where
       $($parser: Parser<I, $output, E>),+
     {
@@ -998,6 +1164,7 @@
 ///
 /// [`Parser`]s will need to use [`Recoverable<I, _>`] for their input.
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 pub trait RecoverableParser<I, O, R, E> {
     /// Collect all errors when parsing the input
     ///
@@ -1010,6 +1177,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<P, I, O, R, E> RecoverableParser<I, O, R, E> for P
 where
     P: Parser<Recoverable<I, R>, O, E>,
@@ -1021,7 +1189,6 @@
     E: ParserError<Recoverable<I, R>>,
     E: crate::lib::std::fmt::Debug,
 {
-    #[inline]
     fn recoverable_parse(&mut self, input: I) -> (I, Option<O>, Vec<R>) {
         debug_assert!(
             !I::is_partial_supported(),
@@ -1033,7 +1200,7 @@
         let start_token = input.checkpoint();
         let result = (
             self.by_ref(),
-            crate::combinator::eof.resume_after(rest.void()),
+            crate::combinator::eof.resume_after(crate::token::rest.void()),
         )
             .parse_next(&mut input);
 
@@ -1050,7 +1217,7 @@
         };
 
         let (mut input, mut errs) = input.into_parts();
-        input.reset(start);
+        input.reset(&start);
         if let Some(err) = err {
             errs.push(err);
         }
@@ -1084,18 +1251,19 @@
 );
 
 #[cfg(feature = "alloc")]
-use alloc::boxed::Box;
+use crate::lib::std::boxed::Box;
 
 #[cfg(feature = "alloc")]
-impl<'a, I, O, E> Parser<I, O, E> for Box<dyn Parser<I, O, E> + 'a> {
+impl<I, O, E> Parser<I, O, E> for Box<dyn Parser<I, O, E> + '_> {
     #[inline(always)]
     fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
         (**self).parse_next(i)
     }
 }
 
-/// Convert a [`Parser::parse_peek`] style parse function to be a [`Parser`]
+/// Deprecated
 #[inline(always)]
+#[deprecated(since = "0.6.23")]
 pub fn unpeek<'a, I, O, E>(
     mut peek: impl FnMut(I) -> IResult<I, O, E> + 'a,
 ) -> impl FnMut(&mut I) -> PResult<O, E>
diff --git a/crates/winnow/src/stream/impls.rs b/crates/winnow/src/stream/impls.rs
index d76e1bf..1cc295e 100644
--- a/crates/winnow/src/stream/impls.rs
+++ b/crates/winnow/src/stream/impls.rs
@@ -1,6 +1,7 @@
 macro_rules! impl_partial_eq {
     ($lhs:ty, $rhs:ty) => {
-        impl<'a, 'b> PartialEq<$rhs> for $lhs {
+        #[allow(unused_lifetimes)]
+        impl<'a> PartialEq<$rhs> for $lhs {
             #[inline]
             fn eq(&self, other: &$rhs) -> bool {
                 let l = self.as_ref();
@@ -9,7 +10,8 @@
             }
         }
 
-        impl<'a, 'b> PartialEq<$lhs> for $rhs {
+        #[allow(unused_lifetimes)]
+        impl<'a> PartialEq<$lhs> for $rhs {
             #[inline]
             fn eq(&self, other: &$lhs) -> bool {
                 PartialEq::eq(other, self)
@@ -20,7 +22,8 @@
 
 macro_rules! impl_partial_ord {
     ($lhs:ty, $rhs:ty) => {
-        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
+        #[allow(unused_lifetimes)]
+        impl<'a> PartialOrd<$rhs> for $lhs {
             #[inline]
             fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
                 let l = self.as_ref();
@@ -29,7 +32,8 @@
             }
         }
 
-        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
+        #[allow(unused_lifetimes)]
+        impl<'a> PartialOrd<$lhs> for $rhs {
             #[inline]
             fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
                 PartialOrd::partial_cmp(other, self)
@@ -61,7 +65,7 @@
         #[inline]
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             for byte in self.as_bytes() {
-                write!(f, "{:0>2x}", byte)?;
+                write!(f, "{byte:0>2x}")?;
             }
             Ok(())
         }
@@ -77,7 +81,7 @@
                         write!(f, "_")?;
                     }
                 }
-                write!(f, "{:0>2X}", byte)?;
+                write!(f, "{byte:0>2X}")?;
             }
             Ok(())
         }
@@ -283,7 +287,7 @@
         #[test]
         fn test_pretty_debug() {
             // Output can change from run-to-run
-            format!(
+            let _ = format!(
                 "{:#?}",
                 Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp")
             );
@@ -293,9 +297,9 @@
         fn test_sliced() {
             // Output can change from run-to-run
             let total = Bytes::new(b"12345678901234567890");
-            format!("{:#?}", total);
-            format!("{:#?}", &total[1..]);
-            format!("{:#?}", &total[10..]);
+            let _ = format!("{total:#?}");
+            let _ = format!("{:#?}", &total[1..]);
+            let _ = format!("{:#?}", &total[10..]);
         }
     }
 }
diff --git a/crates/winnow/src/stream/mod.rs b/crates/winnow/src/stream/mod.rs
index 6873ac3..45470fb 100644
--- a/crates/winnow/src/stream/mod.rs
+++ b/crates/winnow/src/stream/mod.rs
@@ -3,7 +3,7 @@
 //! Stream types include:
 //! - `&[u8]` and [`Bytes`] for binary data
 //! - `&str` (aliased as [`Str`]) and [`BStr`] for UTF-8 data
-//! - [`Located`] can track the location within the original buffer to report
+//! - [`LocatingSlice`] can track the location within the original buffer to report
 //!   [spans][crate::Parser::with_span]
 //! - [`Stateful`] to thread global state through your parsers
 //! - [`Partial`] can mark an input as partial buffer that is being streamed into
@@ -14,6 +14,7 @@
 
 use crate::ascii::Caseless as AsciiCaseless;
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 use crate::error::FromRecoverableError;
 use crate::error::Needed;
 use crate::lib::std::iter::{Cloned, Enumerate};
@@ -47,7 +48,7 @@
 pub type Str<'i> = &'i str;
 
 /// Improved `Debug` experience for `&[u8]` byte streams
-#[allow(clippy::derive_hash_xor_eq)]
+#[allow(clippy::derived_hash_with_manual_eq)]
 #[derive(Hash)]
 #[repr(transparent)]
 pub struct Bytes([u8]);
@@ -71,7 +72,7 @@
 }
 
 /// Improved `Debug` experience for `&[u8]` UTF-8-ish streams
-#[allow(clippy::derive_hash_xor_eq)]
+#[allow(clippy::derived_hash_with_manual_eq)]
 #[derive(Hash)]
 #[repr(transparent)]
 pub struct BStr([u8]);
@@ -94,7 +95,11 @@
     }
 }
 
-/// Allow collecting the span of a parsed token
+/// Deprecated, replaced with [`LocatingSlice`]
+#[deprecated(since = "0.6.23", note = "Replaced with `LocatingSlice`")]
+pub type Located<I> = LocatingSlice<I>;
+
+/// Allow collecting the span of a parsed token within a slice
 ///
 /// Spans are tracked as a [`Range<usize>`] of byte offsets.
 ///
@@ -106,14 +111,15 @@
 /// byte offsets to line numbers.
 ///
 /// See [`Parser::span`][crate::Parser::span] and [`Parser::with_span`][crate::Parser::with_span] for more details
-#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
-#[doc(alias = "LocatedSpan")]
-pub struct Located<I> {
+#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
+#[doc(alias = "LocatingSliceSpan")]
+#[doc(alias = "Located")]
+pub struct LocatingSlice<I> {
     initial: I,
     input: I,
 }
 
-impl<I> Located<I>
+impl<I> LocatingSlice<I>
 where
     I: Clone + Offset,
 {
@@ -123,19 +129,36 @@
         Self { initial, input }
     }
 
+    #[inline]
     fn location(&self) -> usize {
         self.input.offset_from(&self.initial)
     }
 }
 
-impl<I> AsRef<I> for Located<I> {
+impl<I> LocatingSlice<I>
+where
+    I: Clone + Stream + Offset,
+{
+    /// Reset the stream to the start
+    ///
+    /// This is useful for formats that encode a graph with addresses relative to the start of the
+    /// input.
+    #[doc(alias = "fseek")]
+    #[inline]
+    pub fn reset_to_start(&mut self) {
+        let start = self.initial.checkpoint();
+        self.input.reset(&start);
+    }
+}
+
+impl<I> AsRef<I> for LocatingSlice<I> {
     #[inline(always)]
     fn as_ref(&self) -> &I {
         &self.input
     }
 }
 
-impl<I> crate::lib::std::ops::Deref for Located<I> {
+impl<I> crate::lib::std::ops::Deref for LocatingSlice<I> {
     type Target = I;
 
     #[inline(always)]
@@ -144,7 +167,14 @@
     }
 }
 
-impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for Located<I> {
+impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for LocatingSlice<I> {
+    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+        self.input.fmt(f)
+    }
+}
+
+impl<I: crate::lib::std::fmt::Debug> crate::lib::std::fmt::Debug for LocatingSlice<I> {
+    #[inline]
     fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
         self.input.fmt(f)
     }
@@ -155,7 +185,8 @@
 /// Generally, this will be used indirectly via
 /// [`RecoverableParser::recoverable_parse`][crate::RecoverableParser::recoverable_parse].
 #[cfg(feature = "unstable-recover")]
-#[derive(Clone, Debug)]
+#[derive(Clone)]
+#[cfg(feature = "std")]
 pub struct Recoverable<I, E>
 where
     I: Stream,
@@ -166,11 +197,25 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+impl<I, E> Default for Recoverable<I, E>
+where
+    I: Default + Stream,
+{
+    #[inline]
+    fn default() -> Self {
+        Self::new(I::default())
+    }
+}
+
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> Recoverable<I, E>
 where
     I: Stream,
 {
     /// Track recoverable errors with the stream
+    #[inline]
     pub fn new(input: I) -> Self {
         Self {
             input,
@@ -180,6 +225,7 @@
     }
 
     /// Act as a normal stream
+    #[inline]
     pub fn unrecoverable(input: I) -> Self {
         Self {
             input,
@@ -189,12 +235,14 @@
     }
 
     /// Access the current input and errors
+    #[inline]
     pub fn into_parts(self) -> (I, Vec<E>) {
         (self.input, self.errors)
     }
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> AsRef<I> for Recoverable<I, E>
 where
     I: Stream,
@@ -206,6 +254,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> crate::lib::std::ops::Deref for Recoverable<I, E>
 where
     I: Stream,
@@ -219,6 +268,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I: crate::lib::std::fmt::Display, E> crate::lib::std::fmt::Display for Recoverable<I, E>
 where
     I: Stream,
@@ -228,6 +278,25 @@
     }
 }
 
+#[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
+impl<I: Stream + crate::lib::std::fmt::Debug, E: crate::lib::std::fmt::Debug>
+    crate::lib::std::fmt::Debug for Recoverable<I, E>
+{
+    #[inline]
+    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+        if f.alternate() {
+            self.input.fmt(f)
+        } else {
+            f.debug_struct("Recoverable")
+                .field("input", &self.input)
+                .field("errors", &self.errors)
+                .field("is_recoverable", &self.is_recoverable)
+                .finish()
+        }
+    }
+}
+
 /// Thread global state through your parsers
 ///
 /// Use cases
@@ -244,12 +313,12 @@
 /// # use winnow::ascii::alpha1;
 /// # type Error = ();
 ///
-/// #[derive(Clone, Debug)]
-/// struct State<'s>(&'s Cell<u32>);
+/// #[derive(Debug)]
+/// struct State<'s>(&'s mut u32);
 ///
 /// impl<'s> State<'s> {
-///     fn count(&self) {
-///         self.0.set(self.0.get() + 1);
+///     fn count(&mut self) {
+///         *self.0 += 1;
 ///     }
 /// }
 ///
@@ -261,13 +330,13 @@
 /// }
 ///
 /// let data = "Hello";
-/// let state = Cell::new(0);
-/// let input = Stream { input: data, state: State(&state) };
+/// let mut state = 0;
+/// let input = Stream { input: data, state: State(&mut state) };
 /// let output = word.parse(input).unwrap();
-/// assert_eq!(state.get(), 1);
+/// assert_eq!(state, 1);
 /// ```
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[doc(alias = "LocatedSpan")]
+#[derive(Clone, Copy, Default, Eq, PartialEq)]
+#[doc(alias = "LocatingSliceSpan")]
 pub struct Stateful<I, S> {
     /// Inner input being wrapped in state
     pub input: I,
@@ -297,6 +366,22 @@
     }
 }
 
+impl<I: crate::lib::std::fmt::Debug, S: crate::lib::std::fmt::Debug> crate::lib::std::fmt::Debug
+    for Stateful<I, S>
+{
+    #[inline]
+    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+        if f.alternate() {
+            self.input.fmt(f)
+        } else {
+            f.debug_struct("Stateful")
+                .field("input", &self.input)
+                .field("state", &self.state)
+                .finish()
+        }
+    }
+}
+
 /// Mark the input as a partial buffer for streaming input.
 ///
 /// Complete input means that we already have all of the data. This will be the common case with
@@ -340,7 +425,7 @@
 /// // but the complete parser will return an error
 /// assert_eq!(take_complete.parse_peek(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
 ///
-/// // the alpha0 function recognizes 0 or more alphabetic characters
+/// // the alpha0 function takes 0 or more alphabetic characters
 /// fn alpha0_partial<'s>(i: &mut Partial<&'s str>) -> PResult<&'s str, InputError<Partial<&'s str>>> {
 ///   ascii::alpha0.parse_next(i)
 /// }
@@ -349,19 +434,19 @@
 ///   ascii::alpha0.parse_next(i)
 /// }
 ///
-/// // if there's a clear limit to the recognized characters, both parsers work the same way
+/// // if there's a clear limit to the taken characters, both parsers work the same way
 /// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd;")), Ok((Partial::new(";"), "abcd")));
 /// assert_eq!(alpha0_complete.parse_peek("abcd;"), Ok((";", "abcd")));
 ///
 /// // but when there's no limit, the partial version returns `Incomplete`, because it cannot
-/// // know if more input data should be recognized. The whole input could be "abcd;", or
+/// // know if more input data should be taken. The whole input could be "abcd;", or
 /// // "abcde;"
 /// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd")), Err(ErrMode::Incomplete(Needed::new(1))));
 ///
 /// // while the complete version knows that all of the data is there
 /// assert_eq!(alpha0_complete.parse_peek("abcd"), Ok(("", "abcd")));
 /// ```
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub struct Partial<I> {
     input: I,
     partial: bool,
@@ -372,6 +457,7 @@
     I: StreamIsPartial,
 {
     /// Create a partial input
+    #[inline]
     pub fn new(input: I) -> Self {
         debug_assert!(
             !I::is_partial_supported(),
@@ -392,6 +478,7 @@
 where
     I: Default + StreamIsPartial,
 {
+    #[inline]
     fn default() -> Self {
         Self::new(I::default())
     }
@@ -412,6 +499,20 @@
     }
 }
 
+impl<I: crate::lib::std::fmt::Debug> crate::lib::std::fmt::Debug for Partial<I> {
+    #[inline]
+    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+        if f.alternate() {
+            self.input.fmt(f)
+        } else {
+            f.debug_struct("Partial")
+                .field("input", &self.input)
+                .field("partial", &self.partial)
+                .finish()
+        }
+    }
+}
+
 /// Abstract method to calculate the input length
 pub trait SliceLen {
     /// Calculates the input length, as indicated by its name,
@@ -426,50 +527,57 @@
     }
 }
 
-impl<'a, T> SliceLen for &'a [T] {
-    #[inline]
+impl<T> SliceLen for &[T] {
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len()
     }
 }
 
 impl<T, const LEN: usize> SliceLen for [T; LEN] {
-    #[inline]
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len()
     }
 }
 
-impl<'a, T, const LEN: usize> SliceLen for &'a [T; LEN] {
-    #[inline]
+impl<T, const LEN: usize> SliceLen for &[T; LEN] {
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len()
     }
 }
 
-impl<'a> SliceLen for &'a str {
-    #[inline]
+impl SliceLen for &str {
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len()
     }
 }
 
+impl SliceLen for u8 {
+    #[inline(always)]
+    fn slice_len(&self) -> usize {
+        1
+    }
+}
+
 impl SliceLen for char {
-    #[inline]
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len_utf8()
     }
 }
 
-impl<'a> SliceLen for &'a Bytes {
-    #[inline]
+impl SliceLen for &Bytes {
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len()
     }
 }
 
-impl<'a> SliceLen for &'a BStr {
-    #[inline]
+impl SliceLen for &BStr {
+    #[inline(always)]
     fn slice_len(&self) -> usize {
         self.len()
     }
@@ -485,7 +593,7 @@
     }
 }
 
-impl<I> SliceLen for Located<I>
+impl<I> SliceLen for LocatingSlice<I>
 where
     I: SliceLen,
 {
@@ -496,6 +604,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> SliceLen for Recoverable<I, E>
 where
     I: SliceLen,
@@ -535,7 +644,7 @@
     type Token: crate::lib::std::fmt::Debug;
     /// Sequence of `Token`s
     ///
-    /// Example: `&[u8]` for `Located<&[u8]>` or `&str` for `Located<&str>`
+    /// Example: `&[u8]` for `LocatingSlice<&[u8]>` or `&str` for `LocatingSlice<&str>`
     type Slice: crate::lib::std::fmt::Debug;
 
     /// Iterate with the offset from the current location
@@ -573,13 +682,17 @@
     fn offset_at(&self, tokens: usize) -> Result<usize, Needed>;
     /// Split off a slice of tokens from the input
     ///
-    /// **NOTE:** For inputs with variable width tokens, like `&str`'s `char`, `offset` might not correspond
+    /// <div class="warning">
+    ///
+    /// **Note:** For inputs with variable width tokens, like `&str`'s `char`, `offset` might not correspond
     /// with the number of tokens. To get a valid offset, use:
     /// - [`Stream::eof_offset`]
     /// - [`Stream::iter_offsets`]
     /// - [`Stream::offset_for`]
     /// - [`Stream::offset_at`]
     ///
+    /// </div>
+    ///
     /// # Panic
     ///
     /// This will panic if
@@ -623,7 +736,7 @@
     /// # Panic
     ///
     /// May panic if an invalid [`Self::Checkpoint`] is provided
-    fn reset(&mut self, checkpoint: Self::Checkpoint);
+    fn reset(&mut self, checkpoint: &Self::Checkpoint);
 
     /// Return the inner-most stream
     fn raw(&self) -> &dyn crate::lib::std::fmt::Debug;
@@ -638,7 +751,7 @@
 
     type IterOffsets = Enumerate<Cloned<Iter<'i, T>>>;
 
-    type Checkpoint = Checkpoint<Self>;
+    type Checkpoint = Checkpoint<Self, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -680,11 +793,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(*self)
+        Checkpoint::<_, Self>::new(*self)
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        *self = checkpoint.0;
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        *self = checkpoint.inner;
     }
 
     #[inline(always)]
@@ -699,7 +812,7 @@
 
     type IterOffsets = CharIndices<'i>;
 
-    type Checkpoint = Checkpoint<Self>;
+    type Checkpoint = Checkpoint<Self, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -755,11 +868,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(*self)
+        Checkpoint::<_, Self>::new(*self)
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        *self = checkpoint.0;
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        *self = checkpoint.inner;
     }
 
     #[inline(always)]
@@ -774,7 +887,7 @@
 
     type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>;
 
-    type Checkpoint = Checkpoint<Self>;
+    type Checkpoint = Checkpoint<Self, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -820,11 +933,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(*self)
+        Checkpoint::<_, Self>::new(*self)
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        *self = checkpoint.0;
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        *self = checkpoint.inner;
     }
 
     #[inline(always)]
@@ -839,7 +952,7 @@
 
     type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>;
 
-    type Checkpoint = Checkpoint<Self>;
+    type Checkpoint = Checkpoint<Self, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -885,11 +998,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(*self)
+        Checkpoint::<_, Self>::new(*self)
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        *self = checkpoint.0;
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        *self = checkpoint.inner;
     }
 
     #[inline(always)]
@@ -907,7 +1020,7 @@
 
     type IterOffsets = BitOffsets<I>;
 
-    type Checkpoint = Checkpoint<(I::Checkpoint, usize)>;
+    type Checkpoint = Checkpoint<(I::Checkpoint, usize), Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -962,12 +1075,12 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint((self.0.checkpoint(), self.1))
+        Checkpoint::<_, Self>::new((self.0.checkpoint(), self.1))
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        self.0.reset(checkpoint.0 .0);
-        self.1 = checkpoint.0 .1;
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        self.0.reset(&checkpoint.inner.0);
+        self.1 = checkpoint.inner.1;
     }
 
     #[inline(always)]
@@ -1021,13 +1134,13 @@
     }
 }
 
-impl<I: Stream> Stream for Located<I> {
+impl<I: Stream> Stream for LocatingSlice<I> {
     type Token = <I as Stream>::Token;
     type Slice = <I as Stream>::Slice;
 
     type IterOffsets = <I as Stream>::IterOffsets;
 
-    type Checkpoint = Checkpoint<I::Checkpoint>;
+    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -1061,11 +1174,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(self.input.checkpoint())
+        Checkpoint::<_, Self>::new(self.input.checkpoint())
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        self.input.reset(checkpoint.0);
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        self.input.reset(&checkpoint.inner);
     }
 
     #[inline(always)]
@@ -1075,6 +1188,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E: crate::lib::std::fmt::Debug> Stream for Recoverable<I, E>
 where
     I: Stream,
@@ -1084,7 +1198,7 @@
 
     type IterOffsets = <I as Stream>::IterOffsets;
 
-    type Checkpoint = Checkpoint<I::Checkpoint>;
+    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -1118,11 +1232,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(self.input.checkpoint())
+        Checkpoint::<_, Self>::new(self.input.checkpoint())
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        self.input.reset(checkpoint.0);
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        self.input.reset(&checkpoint.inner);
     }
 
     #[inline(always)]
@@ -1137,7 +1251,7 @@
 
     type IterOffsets = <I as Stream>::IterOffsets;
 
-    type Checkpoint = Checkpoint<I::Checkpoint>;
+    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -1171,11 +1285,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(self.input.checkpoint())
+        Checkpoint::<_, Self>::new(self.input.checkpoint())
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        self.input.reset(checkpoint.0);
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        self.input.reset(&checkpoint.inner);
     }
 
     #[inline(always)]
@@ -1190,7 +1304,7 @@
 
     type IterOffsets = <I as Stream>::IterOffsets;
 
-    type Checkpoint = Checkpoint<I::Checkpoint>;
+    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
 
     #[inline(always)]
     fn iter_offsets(&self) -> Self::IterOffsets {
@@ -1224,11 +1338,11 @@
 
     #[inline(always)]
     fn checkpoint(&self) -> Self::Checkpoint {
-        Checkpoint(self.input.checkpoint())
+        Checkpoint::<_, Self>::new(self.input.checkpoint())
     }
     #[inline(always)]
-    fn reset(&mut self, checkpoint: Self::Checkpoint) {
-        self.input.reset(checkpoint.0);
+    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
+        self.input.reset(&checkpoint.inner);
     }
 
     #[inline(always)]
@@ -1239,13 +1353,13 @@
 
 /// Number of indices input has advanced since start of parsing
 ///
-/// See [`Located`] for adding location tracking to your [`Stream`]
+/// See [`LocatingSlice`] for adding location tracking to your [`Stream`]
 pub trait Location {
     /// Number of indices input has advanced since start of parsing
     fn location(&self) -> usize;
 }
 
-impl<I> Location for Located<I>
+impl<I> Location for LocatingSlice<I>
 where
     I: Clone + Offset,
 {
@@ -1256,6 +1370,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> Location for Recoverable<I, E>
 where
     I: Location,
@@ -1291,6 +1406,7 @@
 ///
 /// See [`Recoverable`] for adding error recovery tracking to your [`Stream`]
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 pub trait Recover<E>: Stream {
     /// Capture a top-level error
     ///
@@ -1308,6 +1424,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<'a, T, E> Recover<E> for &'a [T]
 where
     &'a [T]: Stream,
@@ -1330,7 +1447,8 @@
 }
 
 #[cfg(feature = "unstable-recover")]
-impl<'a, E> Recover<E> for &'a str {
+#[cfg(feature = "std")]
+impl<E> Recover<E> for &str {
     #[inline(always)]
     fn record_err(
         &mut self,
@@ -1349,7 +1467,8 @@
 }
 
 #[cfg(feature = "unstable-recover")]
-impl<'a, E> Recover<E> for &'a Bytes {
+#[cfg(feature = "std")]
+impl<E> Recover<E> for &Bytes {
     #[inline(always)]
     fn record_err(
         &mut self,
@@ -1368,7 +1487,8 @@
 }
 
 #[cfg(feature = "unstable-recover")]
-impl<'a, E> Recover<E> for &'a BStr {
+#[cfg(feature = "std")]
+impl<E> Recover<E> for &BStr {
     #[inline(always)]
     fn record_err(
         &mut self,
@@ -1387,6 +1507,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> Recover<E> for (I, usize)
 where
     I: Recover<E>,
@@ -1410,7 +1531,8 @@
 }
 
 #[cfg(feature = "unstable-recover")]
-impl<I, E> Recover<E> for Located<I>
+#[cfg(feature = "std")]
+impl<I, E> Recover<E> for LocatingSlice<I>
 where
     I: Recover<E>,
     I: Stream,
@@ -1433,6 +1555,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E, R> Recover<E> for Recoverable<I, R>
 where
     I: Stream,
@@ -1467,6 +1590,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E, S> Recover<E> for Stateful<I, S>
 where
     I: Recover<E>,
@@ -1491,6 +1615,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> Recover<E> for Partial<I>
 where
     I: Recover<E>,
@@ -1537,11 +1662,13 @@
     }
 }
 
-impl<'a, T> StreamIsPartial for &'a [T] {
+impl<T> StreamIsPartial for &[T] {
     type PartialState = ();
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {}
 
+    #[inline]
     fn restore_partial(&mut self, _state: Self::PartialState) {}
 
     #[inline(always)]
@@ -1550,13 +1677,15 @@
     }
 }
 
-impl<'a> StreamIsPartial for &'a str {
+impl StreamIsPartial for &str {
     type PartialState = ();
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         // Already complete
     }
 
+    #[inline]
     fn restore_partial(&mut self, _state: Self::PartialState) {}
 
     #[inline(always)]
@@ -1565,13 +1694,15 @@
     }
 }
 
-impl<'a> StreamIsPartial for &'a Bytes {
+impl StreamIsPartial for &Bytes {
     type PartialState = ();
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         // Already complete
     }
 
+    #[inline]
     fn restore_partial(&mut self, _state: Self::PartialState) {}
 
     #[inline(always)]
@@ -1580,13 +1711,15 @@
     }
 }
 
-impl<'a> StreamIsPartial for &'a BStr {
+impl StreamIsPartial for &BStr {
     type PartialState = ();
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         // Already complete
     }
 
+    #[inline]
     fn restore_partial(&mut self, _state: Self::PartialState) {}
 
     #[inline(always)]
@@ -1601,10 +1734,12 @@
 {
     type PartialState = I::PartialState;
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         self.0.complete()
     }
 
+    #[inline]
     fn restore_partial(&mut self, state: Self::PartialState) {
         self.0.restore_partial(state);
     }
@@ -1620,16 +1755,18 @@
     }
 }
 
-impl<I> StreamIsPartial for Located<I>
+impl<I> StreamIsPartial for LocatingSlice<I>
 where
     I: StreamIsPartial,
 {
     type PartialState = I::PartialState;
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         self.input.complete()
     }
 
+    #[inline]
     fn restore_partial(&mut self, state: Self::PartialState) {
         self.input.restore_partial(state);
     }
@@ -1646,6 +1783,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> StreamIsPartial for Recoverable<I, E>
 where
     I: StreamIsPartial,
@@ -1653,10 +1791,12 @@
 {
     type PartialState = I::PartialState;
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         self.input.complete()
     }
 
+    #[inline]
     fn restore_partial(&mut self, state: Self::PartialState) {
         self.input.restore_partial(state);
     }
@@ -1678,10 +1818,12 @@
 {
     type PartialState = I::PartialState;
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         self.input.complete()
     }
 
+    #[inline]
     fn restore_partial(&mut self, state: Self::PartialState) {
         self.input.restore_partial(state);
     }
@@ -1703,10 +1845,12 @@
 {
     type PartialState = bool;
 
+    #[inline]
     fn complete(&mut self) -> Self::PartialState {
         core::mem::replace(&mut self.partial, false)
     }
 
+    #[inline]
     fn restore_partial(&mut self, state: Self::PartialState) {
         self.partial = state;
     }
@@ -1726,12 +1870,16 @@
 pub trait Offset<Start = Self> {
     /// Offset between the first byte of `start` and the first byte of `self`a
     ///
+    /// <div class="warning">
+    ///
     /// **Note:** This is an offset, not an index, and may point to the end of input
     /// (`start.len()`) when `self` is exhausted.
+    ///
+    /// </div>
     fn offset_from(&self, start: &Start) -> usize;
 }
 
-impl<'a, T> Offset for &'a [T] {
+impl<T> Offset for &[T] {
     #[inline]
     fn offset_from(&self, start: &Self) -> usize {
         let fst = (*start).as_ptr();
@@ -1755,7 +1903,7 @@
     }
 }
 
-impl<'a> Offset for &'a str {
+impl Offset for &str {
     #[inline(always)]
     fn offset_from(&self, start: &Self) -> usize {
         self.as_bytes().offset_from(&start.as_bytes())
@@ -1769,7 +1917,7 @@
     }
 }
 
-impl<'a> Offset for &'a Bytes {
+impl Offset for &Bytes {
     #[inline(always)]
     fn offset_from(&self, start: &Self) -> usize {
         self.as_bytes().offset_from(&start.as_bytes())
@@ -1783,7 +1931,7 @@
     }
 }
 
-impl<'a> Offset for &'a BStr {
+impl Offset for &BStr {
     #[inline(always)]
     fn offset_from(&self, start: &Self) -> usize {
         self.as_bytes().offset_from(&start.as_bytes())
@@ -1817,7 +1965,7 @@
     }
 }
 
-impl<I> Offset for Located<I>
+impl<I> Offset for LocatingSlice<I>
 where
     I: Stream,
 {
@@ -1827,17 +1975,18 @@
     }
 }
 
-impl<I> Offset<<Located<I> as Stream>::Checkpoint> for Located<I>
+impl<I> Offset<<LocatingSlice<I> as Stream>::Checkpoint> for LocatingSlice<I>
 where
     I: Stream,
 {
     #[inline(always)]
-    fn offset_from(&self, other: &<Located<I> as Stream>::Checkpoint) -> usize {
+    fn offset_from(&self, other: &<LocatingSlice<I> as Stream>::Checkpoint) -> usize {
         self.checkpoint().offset_from(other)
     }
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> Offset for Recoverable<I, E>
 where
     I: Stream,
@@ -1850,6 +1999,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> Offset<<Recoverable<I, E> as Stream>::Checkpoint> for Recoverable<I, E>
 where
     I: Stream,
@@ -1903,13 +2053,13 @@
     }
 }
 
-impl<I> Offset for Checkpoint<I>
+impl<I, S> Offset for Checkpoint<I, S>
 where
     I: Offset,
 {
     #[inline(always)]
     fn offset_from(&self, start: &Self) -> usize {
-        self.0.offset_from(&start.0)
+        self.inner.offset_from(&start.inner)
     }
 }
 
@@ -1919,21 +2069,21 @@
     fn as_bytes(&self) -> &[u8];
 }
 
-impl<'a> AsBytes for &'a [u8] {
+impl AsBytes for &[u8] {
     #[inline(always)]
     fn as_bytes(&self) -> &[u8] {
         self
     }
 }
 
-impl<'a> AsBytes for &'a Bytes {
+impl AsBytes for &Bytes {
     #[inline(always)]
     fn as_bytes(&self) -> &[u8] {
         (*self).as_bytes()
     }
 }
 
-impl<I> AsBytes for Located<I>
+impl<I> AsBytes for LocatingSlice<I>
 where
     I: AsBytes,
 {
@@ -1944,6 +2094,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> AsBytes for Recoverable<I, E>
 where
     I: Stream,
@@ -1981,28 +2132,28 @@
     fn as_bstr(&self) -> &[u8];
 }
 
-impl<'a> AsBStr for &'a [u8] {
+impl AsBStr for &[u8] {
     #[inline(always)]
     fn as_bstr(&self) -> &[u8] {
         self
     }
 }
 
-impl<'a> AsBStr for &'a BStr {
+impl AsBStr for &BStr {
     #[inline(always)]
     fn as_bstr(&self) -> &[u8] {
         (*self).as_bytes()
     }
 }
 
-impl<'a> AsBStr for &'a str {
+impl AsBStr for &str {
     #[inline(always)]
     fn as_bstr(&self) -> &[u8] {
         (*self).as_bytes()
     }
 }
 
-impl<I> AsBStr for Located<I>
+impl<I> AsBStr for LocatingSlice<I>
 where
     I: AsBStr,
 {
@@ -2013,6 +2164,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> AsBStr for Recoverable<I, E>
 where
     I: Stream,
@@ -2048,7 +2200,11 @@
 #[derive(Debug, Eq, PartialEq)]
 pub enum CompareResult {
     /// Comparison was successful
-    Ok,
+    ///
+    /// `usize` is the end of the successful match within the buffer.
+    /// This is most relevant for caseless UTF-8 where `Compare::compare`'s parameter might be a different
+    /// length than the match within the buffer.
+    Ok(usize),
     /// We need more data to be sure
     Incomplete,
     /// Comparison failed
@@ -2059,18 +2215,9 @@
 pub trait Compare<T> {
     /// Compares self to another value for equality
     fn compare(&self, t: T) -> CompareResult;
-    /// Compares self to another value for equality
-    /// independently of the case.
-    ///
-    /// Warning: for `&str`, the comparison is done
-    /// by lowercasing both strings and comparing
-    /// the result. This is a temporary solution until
-    /// a better one appears
-    #[deprecated(since = "0.5.20", note = "Replaced with `compare(ascii::Caseless(_))`")]
-    fn compare_no_case(&self, t: T) -> CompareResult;
 }
 
-impl<'a, 'b> Compare<&'b [u8]> for &'a [u8] {
+impl<'b> Compare<&'b [u8]> for &[u8] {
     #[inline]
     fn compare(&self, t: &'b [u8]) -> CompareResult {
         if t.iter().zip(*self).any(|(a, b)| a != b) {
@@ -2078,18 +2225,12 @@
         } else if self.len() < t.slice_len() {
             CompareResult::Incomplete
         } else {
-            CompareResult::Ok
+            CompareResult::Ok(t.slice_len())
         }
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
-        self.compare(AsciiCaseless(t))
-    }
 }
 
-impl<'a, 'b> Compare<AsciiCaseless<&'b [u8]>> for &'a [u8] {
+impl<'b> Compare<AsciiCaseless<&'b [u8]>> for &[u8] {
     #[inline]
     fn compare(&self, t: AsciiCaseless<&'b [u8]>) -> CompareResult {
         if t.0
@@ -2101,192 +2242,114 @@
         } else if self.len() < t.slice_len() {
             CompareResult::Incomplete
         } else {
-            CompareResult::Ok
+            CompareResult::Ok(t.slice_len())
         }
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<&'b [u8]>) -> CompareResult {
-        self.compare(t)
-    }
 }
 
-impl<'a, const LEN: usize> Compare<[u8; LEN]> for &'a [u8] {
+impl<const LEN: usize> Compare<[u8; LEN]> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: [u8; LEN]) -> CompareResult {
         self.compare(&t[..])
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: [u8; LEN]) -> CompareResult {
-        self.compare_no_case(&t[..])
-    }
 }
 
-impl<'a, const LEN: usize> Compare<AsciiCaseless<[u8; LEN]>> for &'a [u8] {
+impl<const LEN: usize> Compare<AsciiCaseless<[u8; LEN]>> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: AsciiCaseless<[u8; LEN]>) -> CompareResult {
         self.compare(AsciiCaseless(&t.0[..]))
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<[u8; LEN]>) -> CompareResult {
-        self.compare_no_case(AsciiCaseless(&t.0[..]))
-    }
 }
 
-impl<'a, 'b, const LEN: usize> Compare<&'b [u8; LEN]> for &'a [u8] {
+impl<'b, const LEN: usize> Compare<&'b [u8; LEN]> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: &'b [u8; LEN]) -> CompareResult {
         self.compare(&t[..])
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: &'b [u8; LEN]) -> CompareResult {
-        self.compare_no_case(&t[..])
-    }
 }
 
-impl<'a, 'b, const LEN: usize> Compare<AsciiCaseless<&'b [u8; LEN]>> for &'a [u8] {
+impl<'b, const LEN: usize> Compare<AsciiCaseless<&'b [u8; LEN]>> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: AsciiCaseless<&'b [u8; LEN]>) -> CompareResult {
         self.compare(AsciiCaseless(&t.0[..]))
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<&'b [u8; LEN]>) -> CompareResult {
-        self.compare_no_case(AsciiCaseless(&t.0[..]))
-    }
 }
 
-impl<'a, 'b> Compare<&'b str> for &'a [u8] {
+impl<'b> Compare<&'b str> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: &'b str) -> CompareResult {
         self.compare(t.as_bytes())
     }
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: &'b str) -> CompareResult {
-        self.compare_no_case(t.as_bytes())
-    }
 }
 
-impl<'a, 'b> Compare<AsciiCaseless<&'b str>> for &'a [u8] {
+impl<'b> Compare<AsciiCaseless<&'b str>> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: AsciiCaseless<&'b str>) -> CompareResult {
         self.compare(AsciiCaseless(t.0.as_bytes()))
     }
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<&'b str>) -> CompareResult {
-        self.compare_no_case(AsciiCaseless(t.0.as_bytes()))
+}
+
+impl Compare<u8> for &[u8] {
+    #[inline]
+    fn compare(&self, t: u8) -> CompareResult {
+        match self.first().copied() {
+            Some(c) if t == c => CompareResult::Ok(t.slice_len()),
+            Some(_) => CompareResult::Error,
+            None => CompareResult::Incomplete,
+        }
     }
 }
 
-impl<'a> Compare<char> for &'a [u8] {
+impl Compare<AsciiCaseless<u8>> for &[u8] {
+    #[inline]
+    fn compare(&self, t: AsciiCaseless<u8>) -> CompareResult {
+        match self.first() {
+            Some(c) if t.0.eq_ignore_ascii_case(c) => CompareResult::Ok(t.slice_len()),
+            Some(_) => CompareResult::Error,
+            None => CompareResult::Incomplete,
+        }
+    }
+}
+
+impl Compare<char> for &[u8] {
     #[inline(always)]
     fn compare(&self, t: char) -> CompareResult {
         self.compare(t.encode_utf8(&mut [0; 4]).as_bytes())
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: char) -> CompareResult {
-        self.compare_no_case(t.encode_utf8(&mut [0; 4]).as_bytes())
-    }
 }
 
-impl<'a> Compare<AsciiCaseless<char>> for &'a [u8] {
-    #[inline]
+impl Compare<AsciiCaseless<char>> for &[u8] {
+    #[inline(always)]
     fn compare(&self, t: AsciiCaseless<char>) -> CompareResult {
         self.compare(AsciiCaseless(t.0.encode_utf8(&mut [0; 4]).as_bytes()))
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<char>) -> CompareResult {
-        self.compare_no_case(AsciiCaseless(t.0.encode_utf8(&mut [0; 4]).as_bytes()))
-    }
 }
 
-impl<'a, 'b> Compare<&'b str> for &'a str {
+impl<'b> Compare<&'b str> for &str {
     #[inline(always)]
     fn compare(&self, t: &'b str) -> CompareResult {
         self.as_bytes().compare(t.as_bytes())
     }
-
-    #[inline]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: &'b str) -> CompareResult {
-        self.compare(AsciiCaseless(t))
-    }
 }
 
-impl<'a, 'b> Compare<AsciiCaseless<&'b str>> for &'a str {
+impl<'b> Compare<AsciiCaseless<&'b str>> for &str {
     #[inline(always)]
     fn compare(&self, t: AsciiCaseless<&'b str>) -> CompareResult {
         self.as_bytes().compare(t.as_bytes())
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<&'b str>) -> CompareResult {
-        self.compare(t)
-    }
 }
 
-impl<'a> Compare<char> for &'a str {
+impl Compare<char> for &str {
     #[inline(always)]
     fn compare(&self, t: char) -> CompareResult {
-        self.compare(t.encode_utf8(&mut [0; 4]).as_bytes())
-    }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: char) -> CompareResult {
-        self.compare_no_case(t.encode_utf8(&mut [0; 4]).as_bytes())
+        self.as_bytes().compare(t)
     }
 }
 
-impl<'a> Compare<AsciiCaseless<char>> for &'a str {
-    #[inline]
+impl Compare<AsciiCaseless<char>> for &str {
+    #[inline(always)]
     fn compare(&self, t: AsciiCaseless<char>) -> CompareResult {
-        self.compare(AsciiCaseless(t.0.encode_utf8(&mut [0; 4]).as_bytes()))
-    }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<char>) -> CompareResult {
-        self.compare_no_case(AsciiCaseless(t.0.encode_utf8(&mut [0; 4]).as_bytes()))
-    }
-}
-
-impl<'a, 'b> Compare<&'b [u8]> for &'a str {
-    #[inline(always)]
-    fn compare(&self, t: &'b [u8]) -> CompareResult {
-        AsBStr::as_bstr(self).compare(t)
-    }
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
-        AsBStr::as_bstr(self).compare_no_case(t)
-    }
-}
-
-impl<'a, 'b> Compare<AsciiCaseless<&'b [u8]>> for &'a str {
-    #[inline(always)]
-    fn compare(&self, t: AsciiCaseless<&'b [u8]>) -> CompareResult {
-        AsBStr::as_bstr(self).compare(t)
-    }
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: AsciiCaseless<&'b [u8]>) -> CompareResult {
-        AsBStr::as_bstr(self).compare_no_case(t)
+        self.as_bytes().compare(t)
     }
 }
 
@@ -2299,13 +2362,6 @@
         let bytes = (*self).as_bytes();
         bytes.compare(t)
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: T) -> CompareResult {
-        let bytes = (*self).as_bytes();
-        bytes.compare_no_case(t)
-    }
 }
 
 impl<'a, T> Compare<T> for &'a BStr
@@ -2317,16 +2373,9 @@
         let bytes = (*self).as_bytes();
         bytes.compare(t)
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: T) -> CompareResult {
-        let bytes = (*self).as_bytes();
-        bytes.compare_no_case(t)
-    }
 }
 
-impl<I, U> Compare<U> for Located<I>
+impl<I, U> Compare<U> for LocatingSlice<I>
 where
     I: Compare<U>,
 {
@@ -2334,15 +2383,10 @@
     fn compare(&self, other: U) -> CompareResult {
         self.input.compare(other)
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, other: U) -> CompareResult {
-        self.input.compare_no_case(other)
-    }
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E, U> Compare<U> for Recoverable<I, E>
 where
     I: Stream,
@@ -2352,12 +2396,6 @@
     fn compare(&self, other: U) -> CompareResult {
         self.input.compare(other)
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, other: U) -> CompareResult {
-        self.input.compare_no_case(other)
-    }
 }
 
 impl<I, S, U> Compare<U> for Stateful<I, S>
@@ -2368,12 +2406,6 @@
     fn compare(&self, other: U) -> CompareResult {
         self.input.compare(other)
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, other: U) -> CompareResult {
-        self.input.compare_no_case(other)
-    }
 }
 
 impl<I, T> Compare<T> for Partial<I>
@@ -2384,100 +2416,145 @@
     fn compare(&self, t: T) -> CompareResult {
         self.input.compare(t)
     }
-
-    #[inline(always)]
-    #[allow(deprecated)]
-    fn compare_no_case(&self, t: T) -> CompareResult {
-        self.input.compare_no_case(t)
-    }
 }
 
 /// Look for a slice in self
 pub trait FindSlice<T> {
     /// Returns the offset of the slice if it is found
-    fn find_slice(&self, substr: T) -> Option<usize>;
+    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>>;
 }
 
-impl<'i, 's> FindSlice<&'s [u8]> for &'i [u8] {
+impl<'s> FindSlice<&'s [u8]> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: &'s [u8]) -> Option<usize> {
+    fn find_slice(&self, substr: &'s [u8]) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem(self, substr)
     }
 }
 
-impl<'i, 's> FindSlice<(&'s [u8],)> for &'i [u8] {
+impl<'s> FindSlice<(&'s [u8],)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s [u8],)) -> Option<usize> {
+    fn find_slice(&self, substr: (&'s [u8],)) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem(self, substr.0)
     }
 }
 
-impl<'i, 's> FindSlice<(&'s [u8], &'s [u8])> for &'i [u8] {
+impl<'s> FindSlice<(&'s [u8], &'s [u8])> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s [u8], &'s [u8])) -> Option<usize> {
+    fn find_slice(
+        &self,
+        substr: (&'s [u8], &'s [u8]),
+    ) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem2(self, substr)
     }
 }
 
-impl<'i, 's> FindSlice<(&'s [u8], &'s [u8], &'s [u8])> for &'i [u8] {
+impl<'s> FindSlice<(&'s [u8], &'s [u8], &'s [u8])> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s [u8], &'s [u8], &'s [u8])) -> Option<usize> {
+    fn find_slice(
+        &self,
+        substr: (&'s [u8], &'s [u8], &'s [u8]),
+    ) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem3(self, substr)
     }
 }
 
-impl<'i> FindSlice<u8> for &'i [u8] {
+impl FindSlice<char> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: u8) -> Option<usize> {
-        memchr(substr, self)
+    fn find_slice(&self, substr: char) -> Option<crate::lib::std::ops::Range<usize>> {
+        let mut b = [0; 4];
+        let substr = substr.encode_utf8(&mut b);
+        self.find_slice(&*substr)
     }
 }
 
-impl<'i> FindSlice<(u8,)> for &'i [u8] {
+impl FindSlice<(char,)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (u8,)) -> Option<usize> {
-        memchr(substr.0, self)
+    fn find_slice(&self, substr: (char,)) -> Option<crate::lib::std::ops::Range<usize>> {
+        let mut b = [0; 4];
+        let substr0 = substr.0.encode_utf8(&mut b);
+        self.find_slice((&*substr0,))
     }
 }
 
-impl<'i> FindSlice<(u8, u8)> for &'i [u8] {
+impl FindSlice<(char, char)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (u8, u8)) -> Option<usize> {
-        memchr2(substr, self)
+    fn find_slice(&self, substr: (char, char)) -> Option<crate::lib::std::ops::Range<usize>> {
+        let mut b = [0; 4];
+        let substr0 = substr.0.encode_utf8(&mut b);
+        let mut b = [0; 4];
+        let substr1 = substr.1.encode_utf8(&mut b);
+        self.find_slice((&*substr0, &*substr1))
     }
 }
 
-impl<'i> FindSlice<(u8, u8, u8)> for &'i [u8] {
+impl FindSlice<(char, char, char)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (u8, u8, u8)) -> Option<usize> {
-        memchr3(substr, self)
+    fn find_slice(&self, substr: (char, char, char)) -> Option<crate::lib::std::ops::Range<usize>> {
+        let mut b = [0; 4];
+        let substr0 = substr.0.encode_utf8(&mut b);
+        let mut b = [0; 4];
+        let substr1 = substr.1.encode_utf8(&mut b);
+        let mut b = [0; 4];
+        let substr2 = substr.2.encode_utf8(&mut b);
+        self.find_slice((&*substr0, &*substr1, &*substr2))
     }
 }
 
-impl<'i, 's> FindSlice<&'s str> for &'i [u8] {
+impl FindSlice<u8> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: &'s str) -> Option<usize> {
+    fn find_slice(&self, substr: u8) -> Option<crate::lib::std::ops::Range<usize>> {
+        memchr(substr, self).map(|i| i..i + 1)
+    }
+}
+
+impl FindSlice<(u8,)> for &[u8] {
+    #[inline(always)]
+    fn find_slice(&self, substr: (u8,)) -> Option<crate::lib::std::ops::Range<usize>> {
+        memchr(substr.0, self).map(|i| i..i + 1)
+    }
+}
+
+impl FindSlice<(u8, u8)> for &[u8] {
+    #[inline(always)]
+    fn find_slice(&self, substr: (u8, u8)) -> Option<crate::lib::std::ops::Range<usize>> {
+        memchr2(substr, self).map(|i| i..i + 1)
+    }
+}
+
+impl FindSlice<(u8, u8, u8)> for &[u8] {
+    #[inline(always)]
+    fn find_slice(&self, substr: (u8, u8, u8)) -> Option<crate::lib::std::ops::Range<usize>> {
+        memchr3(substr, self).map(|i| i..i + 1)
+    }
+}
+
+impl<'s> FindSlice<&'s str> for &[u8] {
+    #[inline(always)]
+    fn find_slice(&self, substr: &'s str) -> Option<crate::lib::std::ops::Range<usize>> {
         self.find_slice(substr.as_bytes())
     }
 }
 
-impl<'i, 's> FindSlice<(&'s str,)> for &'i [u8] {
+impl<'s> FindSlice<(&'s str,)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s str,)) -> Option<usize> {
+    fn find_slice(&self, substr: (&'s str,)) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem(self, substr.0.as_bytes())
     }
 }
 
-impl<'i, 's> FindSlice<(&'s str, &'s str)> for &'i [u8] {
+impl<'s> FindSlice<(&'s str, &'s str)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s str, &'s str)) -> Option<usize> {
+    fn find_slice(&self, substr: (&'s str, &'s str)) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem2(self, (substr.0.as_bytes(), substr.1.as_bytes()))
     }
 }
 
-impl<'i, 's> FindSlice<(&'s str, &'s str, &'s str)> for &'i [u8] {
+impl<'s> FindSlice<(&'s str, &'s str, &'s str)> for &[u8] {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s str, &'s str, &'s str)) -> Option<usize> {
+    fn find_slice(
+        &self,
+        substr: (&'s str, &'s str, &'s str),
+    ) -> Option<crate::lib::std::ops::Range<usize>> {
         memmem3(
             self,
             (
@@ -2489,101 +2566,62 @@
     }
 }
 
-impl<'i, 's> FindSlice<&'s str> for &'i str {
+impl<'s> FindSlice<&'s str> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: &'s str) -> Option<usize> {
-        self.as_bytes().find_slice(substr.as_bytes())
-    }
-}
-
-impl<'i, 's> FindSlice<(&'s str,)> for &'i str {
-    #[inline(always)]
-    fn find_slice(&self, substr: (&'s str,)) -> Option<usize> {
+    fn find_slice(&self, substr: &'s str) -> Option<crate::lib::std::ops::Range<usize>> {
         self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i, 's> FindSlice<(&'s str, &'s str)> for &'i str {
+impl<'s> FindSlice<(&'s str,)> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s str, &'s str)) -> Option<usize> {
+    fn find_slice(&self, substr: (&'s str,)) -> Option<crate::lib::std::ops::Range<usize>> {
         self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i, 's> FindSlice<(&'s str, &'s str, &'s str)> for &'i str {
+impl<'s> FindSlice<(&'s str, &'s str)> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: (&'s str, &'s str, &'s str)) -> Option<usize> {
+    fn find_slice(&self, substr: (&'s str, &'s str)) -> Option<crate::lib::std::ops::Range<usize>> {
         self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i> FindSlice<char> for &'i str {
+impl<'s> FindSlice<(&'s str, &'s str, &'s str)> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: char) -> Option<usize> {
-        let mut b = [0; 4];
-        let substr = substr.encode_utf8(&mut b);
-        self.find_slice(&*substr)
+    fn find_slice(
+        &self,
+        substr: (&'s str, &'s str, &'s str),
+    ) -> Option<crate::lib::std::ops::Range<usize>> {
+        self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i> FindSlice<(char,)> for &'i str {
+impl FindSlice<char> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: (char,)) -> Option<usize> {
-        let mut b = [0; 4];
-        let substr0 = substr.0.encode_utf8(&mut b);
-        self.find_slice((&*substr0,))
+    fn find_slice(&self, substr: char) -> Option<crate::lib::std::ops::Range<usize>> {
+        self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i> FindSlice<(char, char)> for &'i str {
+impl FindSlice<(char,)> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: (char, char)) -> Option<usize> {
-        let mut b = [0; 4];
-        let substr0 = substr.0.encode_utf8(&mut b);
-        let mut b = [0; 4];
-        let substr1 = substr.1.encode_utf8(&mut b);
-        self.find_slice((&*substr0, &*substr1))
+    fn find_slice(&self, substr: (char,)) -> Option<crate::lib::std::ops::Range<usize>> {
+        self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i> FindSlice<(char, char, char)> for &'i str {
+impl FindSlice<(char, char)> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: (char, char, char)) -> Option<usize> {
-        let mut b = [0; 4];
-        let substr0 = substr.0.encode_utf8(&mut b);
-        let mut b = [0; 4];
-        let substr1 = substr.1.encode_utf8(&mut b);
-        let mut b = [0; 4];
-        let substr2 = substr.2.encode_utf8(&mut b);
-        self.find_slice((&*substr0, &*substr1, &*substr2))
+    fn find_slice(&self, substr: (char, char)) -> Option<crate::lib::std::ops::Range<usize>> {
+        self.as_bytes().find_slice(substr)
     }
 }
 
-impl<'i> FindSlice<u8> for &'i str {
+impl FindSlice<(char, char, char)> for &str {
     #[inline(always)]
-    fn find_slice(&self, substr: u8) -> Option<usize> {
-        self.find_slice(substr.as_char())
-    }
-}
-
-impl<'i> FindSlice<(u8,)> for &'i str {
-    #[inline(always)]
-    fn find_slice(&self, substr: (u8,)) -> Option<usize> {
-        self.find_slice((substr.0.as_char(),))
-    }
-}
-
-impl<'i> FindSlice<(u8, u8)> for &'i str {
-    #[inline(always)]
-    fn find_slice(&self, substr: (u8, u8)) -> Option<usize> {
-        self.find_slice((substr.0.as_char(), substr.1.as_char()))
-    }
-}
-
-impl<'i> FindSlice<(u8, u8, u8)> for &'i str {
-    #[inline(always)]
-    fn find_slice(&self, substr: (u8, u8, u8)) -> Option<usize> {
-        self.find_slice((substr.0.as_char(), substr.1.as_char(), substr.2.as_char()))
+    fn find_slice(&self, substr: (char, char, char)) -> Option<crate::lib::std::ops::Range<usize>> {
+        self.as_bytes().find_slice(substr)
     }
 }
 
@@ -2592,7 +2630,7 @@
     &'i [u8]: FindSlice<S>,
 {
     #[inline(always)]
-    fn find_slice(&self, substr: S) -> Option<usize> {
+    fn find_slice(&self, substr: S) -> Option<crate::lib::std::ops::Range<usize>> {
         let bytes = (*self).as_bytes();
         let offset = bytes.find_slice(substr);
         offset
@@ -2604,31 +2642,32 @@
     &'i [u8]: FindSlice<S>,
 {
     #[inline(always)]
-    fn find_slice(&self, substr: S) -> Option<usize> {
+    fn find_slice(&self, substr: S) -> Option<crate::lib::std::ops::Range<usize>> {
         let bytes = (*self).as_bytes();
         let offset = bytes.find_slice(substr);
         offset
     }
 }
 
-impl<I, T> FindSlice<T> for Located<I>
+impl<I, T> FindSlice<T> for LocatingSlice<I>
 where
     I: FindSlice<T>,
 {
     #[inline(always)]
-    fn find_slice(&self, substr: T) -> Option<usize> {
+    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
         self.input.find_slice(substr)
     }
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E, T> FindSlice<T> for Recoverable<I, E>
 where
     I: Stream,
     I: FindSlice<T>,
 {
     #[inline(always)]
-    fn find_slice(&self, substr: T) -> Option<usize> {
+    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
         self.input.find_slice(substr)
     }
 }
@@ -2638,7 +2677,7 @@
     I: FindSlice<T>,
 {
     #[inline(always)]
-    fn find_slice(&self, substr: T) -> Option<usize> {
+    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
         self.input.find_slice(substr)
     }
 }
@@ -2648,28 +2687,28 @@
     I: FindSlice<T>,
 {
     #[inline(always)]
-    fn find_slice(&self, substr: T) -> Option<usize> {
+    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
         self.input.find_slice(substr)
     }
 }
 
 /// Used to integrate `str`'s `parse()` method
 pub trait ParseSlice<R> {
-    /// Succeeds if `parse()` succeededThe
+    /// Succeeds if `parse()` succeeded
     ///
     /// The byte slice implementation will first convert it to a `&str`, then apply the `parse()`
     /// function
     fn parse_slice(&self) -> Option<R>;
 }
 
-impl<'a, R: FromStr> ParseSlice<R> for &'a [u8] {
+impl<R: FromStr> ParseSlice<R> for &[u8] {
     #[inline(always)]
     fn parse_slice(&self) -> Option<R> {
         from_utf8(self).ok().and_then(|s| s.parse().ok())
     }
 }
 
-impl<'a, R: FromStr> ParseSlice<R> for &'a str {
+impl<R: FromStr> ParseSlice<R> for &str {
     #[inline(always)]
     fn parse_slice(&self) -> Option<R> {
         self.parse().ok()
@@ -2682,7 +2721,7 @@
     fn update_slice(self, inner: Self::Slice) -> Self;
 }
 
-impl<'a, T> UpdateSlice for &'a [T]
+impl<T> UpdateSlice for &[T]
 where
     T: Clone + crate::lib::std::fmt::Debug,
 {
@@ -2692,28 +2731,28 @@
     }
 }
 
-impl<'a> UpdateSlice for &'a str {
+impl UpdateSlice for &str {
     #[inline(always)]
     fn update_slice(self, inner: Self::Slice) -> Self {
         inner
     }
 }
 
-impl<'a> UpdateSlice for &'a Bytes {
+impl UpdateSlice for &Bytes {
     #[inline(always)]
     fn update_slice(self, inner: Self::Slice) -> Self {
         Bytes::new(inner)
     }
 }
 
-impl<'a> UpdateSlice for &'a BStr {
+impl UpdateSlice for &BStr {
     #[inline(always)]
     fn update_slice(self, inner: Self::Slice) -> Self {
         BStr::new(inner)
     }
 }
 
-impl<I> UpdateSlice for Located<I>
+impl<I> UpdateSlice for LocatingSlice<I>
 where
     I: UpdateSlice,
 {
@@ -2725,6 +2764,7 @@
 }
 
 #[cfg(feature = "unstable-recover")]
+#[cfg(feature = "std")]
 impl<I, E> UpdateSlice for Recoverable<I, E>
 where
     I: Stream,
@@ -2764,10 +2804,89 @@
 }
 
 /// Ensure checkpoint details are kept private
-#[derive(Copy, Clone, Debug)]
-pub struct Checkpoint<T>(T);
+pub struct Checkpoint<T, S> {
+    inner: T,
+    stream: core::marker::PhantomData<S>,
+}
+
+impl<T, S> Checkpoint<T, S> {
+    fn new(inner: T) -> Self {
+        Self {
+            inner,
+            stream: Default::default(),
+        }
+    }
+}
+
+impl<T: Copy, S> Copy for Checkpoint<T, S> {}
+
+impl<T: Clone, S> Clone for Checkpoint<T, S> {
+    #[inline(always)]
+    fn clone(&self) -> Self {
+        Self {
+            inner: self.inner.clone(),
+            stream: Default::default(),
+        }
+    }
+}
+
+impl<T: PartialOrd, S> PartialOrd for Checkpoint<T, S> {
+    #[inline(always)]
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        self.inner.partial_cmp(&other.inner)
+    }
+}
+
+impl<T: Ord, S> Ord for Checkpoint<T, S> {
+    #[inline(always)]
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        self.inner.cmp(&other.inner)
+    }
+}
+
+impl<T: PartialEq, S> PartialEq for Checkpoint<T, S> {
+    #[inline(always)]
+    fn eq(&self, other: &Self) -> bool {
+        self.inner.eq(&other.inner)
+    }
+}
+
+impl<T: Eq, S> Eq for Checkpoint<T, S> {}
+
+impl<T: crate::lib::std::fmt::Debug, S> crate::lib::std::fmt::Debug for Checkpoint<T, S> {
+    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+        self.inner.fmt(f)
+    }
+}
 
 /// A range bounded inclusively for counting parses performed
+///
+/// This is flexible in what can be converted to a [Range]:
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::prelude::*;
+/// # use winnow::token::any;
+/// # use winnow::combinator::repeat;
+/// # fn inner(input: &mut &str) -> PResult<char> {
+/// #     any.parse_next(input)
+/// # }
+/// # let mut input = "0123456789012345678901234567890123456789";
+/// # let input = &mut input;
+/// let parser: Vec<_> = repeat(5, inner).parse_next(input).unwrap();
+/// # let mut input = "0123456789012345678901234567890123456789";
+/// # let input = &mut input;
+/// let parser: Vec<_> = repeat(.., inner).parse_next(input).unwrap();
+/// # let mut input = "0123456789012345678901234567890123456789";
+/// # let input = &mut input;
+/// let parser: Vec<_> = repeat(1.., inner).parse_next(input).unwrap();
+/// # let mut input = "0123456789012345678901234567890123456789";
+/// # let input = &mut input;
+/// let parser: Vec<_> = repeat(5..8, inner).parse_next(input).unwrap();
+/// # let mut input = "0123456789012345678901234567890123456789";
+/// # let input = &mut input;
+/// let parser: Vec<_> = repeat(5..=8, inner).parse_next(input).unwrap();
+/// # }
+/// ```
 #[derive(PartialEq, Eq)]
 pub struct Range {
     pub(crate) start_inclusive: usize,
@@ -3128,8 +3247,12 @@
 
     /// Tests that self is an alphabetic character
     ///
-    /// **Warning:** for `&str` it recognizes alphabetic
+    /// <div class="warning">
+    ///
+    /// **Warning:** for `&str` it matches alphabetic
     /// characters outside of the 52 ASCII letters
+    ///
+    /// </div>
     fn is_alpha(self) -> bool;
 
     /// Tests that self is an alphabetic character
@@ -3188,42 +3311,42 @@
     }
 }
 
-impl<'a> AsChar for &'a u8 {
+impl AsChar for &u8 {
     #[inline(always)]
     fn as_char(self) -> char {
-        *self as char
+        (*self).as_char()
     }
-    #[inline]
+    #[inline(always)]
     fn is_alpha(self) -> bool {
-        matches!(*self, 0x41..=0x5A | 0x61..=0x7A)
+        (*self).is_alpha()
     }
-    #[inline]
+    #[inline(always)]
     fn is_alphanum(self) -> bool {
-        self.is_alpha() || self.is_dec_digit()
+        (*self).is_alphanum()
     }
-    #[inline]
+    #[inline(always)]
     fn is_dec_digit(self) -> bool {
-        matches!(*self, 0x30..=0x39)
+        (*self).is_dec_digit()
     }
-    #[inline]
+    #[inline(always)]
     fn is_hex_digit(self) -> bool {
-        matches!(*self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66)
+        (*self).is_hex_digit()
     }
-    #[inline]
+    #[inline(always)]
     fn is_oct_digit(self) -> bool {
-        matches!(*self, 0x30..=0x37)
+        (*self).is_oct_digit()
     }
-    #[inline]
+    #[inline(always)]
     fn len(self) -> usize {
-        1
+        (*self).len()
     }
-    #[inline]
+    #[inline(always)]
     fn is_space(self) -> bool {
-        *self == b' ' || *self == b'\t'
+        (*self).is_space()
     }
-    #[inline]
+    #[inline(always)]
     fn is_newline(self) -> bool {
-        *self == b'\n'
+        (*self).is_newline()
     }
 }
 
@@ -3266,54 +3389,53 @@
     }
 }
 
-impl<'a> AsChar for &'a char {
+impl AsChar for &char {
     #[inline(always)]
     fn as_char(self) -> char {
-        *self
+        (*self).as_char()
     }
-    #[inline]
+    #[inline(always)]
     fn is_alpha(self) -> bool {
-        self.is_ascii_alphabetic()
+        (*self).is_alpha()
     }
-    #[inline]
+    #[inline(always)]
     fn is_alphanum(self) -> bool {
-        self.is_alpha() || self.is_dec_digit()
+        (*self).is_alphanum()
     }
-    #[inline]
+    #[inline(always)]
     fn is_dec_digit(self) -> bool {
-        self.is_ascii_digit()
+        (*self).is_dec_digit()
     }
-    #[inline]
+    #[inline(always)]
     fn is_hex_digit(self) -> bool {
-        self.is_ascii_hexdigit()
+        (*self).is_hex_digit()
     }
-    #[inline]
+    #[inline(always)]
     fn is_oct_digit(self) -> bool {
-        self.is_digit(8)
+        (*self).is_oct_digit()
     }
-    #[inline]
+    #[inline(always)]
     fn len(self) -> usize {
-        self.len_utf8()
+        (*self).len()
     }
-    #[inline]
+    #[inline(always)]
     fn is_space(self) -> bool {
-        *self == ' ' || *self == '\t'
+        (*self).is_space()
     }
-    #[inline]
+    #[inline(always)]
     fn is_newline(self) -> bool {
-        *self == '\n'
+        (*self).is_newline()
     }
 }
 
-/// Check if a token in in a set of possible tokens
+/// Check if a token is in a set of possible tokens
 ///
-/// This is generally implemented on patterns that a token may match and supports `u8` and `char`
-/// tokens along with the following patterns
+/// While this can be implemented manually, you can also build up sets using:
 /// - `b'c'` and `'c'`
-/// - `b""` and `""`
+/// - `b""`
 /// - `|c| true`
 /// - `b'a'..=b'z'`, `'a'..='z'` (etc for each [range type][std::ops])
-/// - `(pattern1, pattern2, ...)`
+/// - `(set1, set2, ...)`
 ///
 /// # Example
 ///
@@ -3342,7 +3464,7 @@
     }
 }
 
-impl<'a> ContainsToken<&'a u8> for u8 {
+impl ContainsToken<&u8> for u8 {
     #[inline(always)]
     fn contains_token(&self, token: &u8) -> bool {
         self.contains_token(*token)
@@ -3356,7 +3478,7 @@
     }
 }
 
-impl<'a> ContainsToken<&'a char> for u8 {
+impl ContainsToken<&char> for u8 {
     #[inline(always)]
     fn contains_token(&self, token: &char) -> bool {
         self.contains_token(*token)
@@ -3558,126 +3680,147 @@
 }
 
 #[inline(always)]
-fn memmem(slice: &[u8], tag: &[u8]) -> Option<usize> {
-    if tag.len() == 1 {
-        memchr(tag[0], slice)
-    } else {
-        memmem_(slice, tag)
+fn memmem(slice: &[u8], literal: &[u8]) -> Option<crate::lib::std::ops::Range<usize>> {
+    match literal.len() {
+        0 => Some(0..0),
+        1 => memchr(literal[0], slice).map(|i| i..i + 1),
+        _ => memmem_(slice, literal),
     }
 }
 
 #[inline(always)]
-fn memmem2(slice: &[u8], tag: (&[u8], &[u8])) -> Option<usize> {
-    if tag.0.len() == 1 && tag.1.len() == 1 {
-        memchr2((tag.0[0], tag.1[0]), slice)
-    } else {
-        memmem2_(slice, tag)
+fn memmem2(slice: &[u8], literal: (&[u8], &[u8])) -> Option<crate::lib::std::ops::Range<usize>> {
+    match (literal.0.len(), literal.1.len()) {
+        (0, _) | (_, 0) => Some(0..0),
+        (1, 1) => memchr2((literal.0[0], literal.1[0]), slice).map(|i| i..i + 1),
+        _ => memmem2_(slice, literal),
     }
 }
 
 #[inline(always)]
-fn memmem3(slice: &[u8], tag: (&[u8], &[u8], &[u8])) -> Option<usize> {
-    if tag.0.len() == 1 && tag.1.len() == 1 && tag.2.len() == 1 {
-        memchr3((tag.0[0], tag.1[0], tag.2[0]), slice)
-    } else {
-        memmem3_(slice, tag)
+fn memmem3(
+    slice: &[u8],
+    literal: (&[u8], &[u8], &[u8]),
+) -> Option<crate::lib::std::ops::Range<usize>> {
+    match (literal.0.len(), literal.1.len(), literal.2.len()) {
+        (0, _, _) | (_, 0, _) | (_, _, 0) => Some(0..0),
+        (1, 1, 1) => memchr3((literal.0[0], literal.1[0], literal.2[0]), slice).map(|i| i..i + 1),
+        _ => memmem3_(slice, literal),
     }
 }
 
 #[cfg(feature = "simd")]
 #[inline(always)]
-fn memmem_(slice: &[u8], tag: &[u8]) -> Option<usize> {
-    let &prefix = match tag.first() {
+fn memmem_(slice: &[u8], literal: &[u8]) -> Option<crate::lib::std::ops::Range<usize>> {
+    let &prefix = match literal.first() {
         Some(x) => x,
-        None => return Some(0),
+        None => return Some(0..0),
     };
     #[allow(clippy::manual_find)] // faster this way
     for i in memchr::memchr_iter(prefix, slice) {
-        if slice[i..].starts_with(tag) {
-            return Some(i);
+        if slice[i..].starts_with(literal) {
+            let i_end = i + literal.len();
+            return Some(i..i_end);
         }
     }
     None
 }
 
 #[cfg(feature = "simd")]
-fn memmem2_(slice: &[u8], tag: (&[u8], &[u8])) -> Option<usize> {
-    let prefix = match (tag.0.first(), tag.1.first()) {
+fn memmem2_(slice: &[u8], literal: (&[u8], &[u8])) -> Option<crate::lib::std::ops::Range<usize>> {
+    let prefix = match (literal.0.first(), literal.1.first()) {
         (Some(&a), Some(&b)) => (a, b),
-        _ => return Some(0),
+        _ => return Some(0..0),
     };
     #[allow(clippy::manual_find)] // faster this way
     for i in memchr::memchr2_iter(prefix.0, prefix.1, slice) {
         let subslice = &slice[i..];
-        if subslice.starts_with(tag.0) {
-            return Some(i);
+        if subslice.starts_with(literal.0) {
+            let i_end = i + literal.0.len();
+            return Some(i..i_end);
         }
-        if subslice.starts_with(tag.1) {
-            return Some(i);
+        if subslice.starts_with(literal.1) {
+            let i_end = i + literal.1.len();
+            return Some(i..i_end);
         }
     }
     None
 }
 
 #[cfg(feature = "simd")]
-fn memmem3_(slice: &[u8], tag: (&[u8], &[u8], &[u8])) -> Option<usize> {
-    let prefix = match (tag.0.first(), tag.1.first(), tag.2.first()) {
+fn memmem3_(
+    slice: &[u8],
+    literal: (&[u8], &[u8], &[u8]),
+) -> Option<crate::lib::std::ops::Range<usize>> {
+    let prefix = match (literal.0.first(), literal.1.first(), literal.2.first()) {
         (Some(&a), Some(&b), Some(&c)) => (a, b, c),
-        _ => return Some(0),
+        _ => return Some(0..0),
     };
     #[allow(clippy::manual_find)] // faster this way
     for i in memchr::memchr3_iter(prefix.0, prefix.1, prefix.2, slice) {
         let subslice = &slice[i..];
-        if subslice.starts_with(tag.0) {
-            return Some(i);
+        if subslice.starts_with(literal.0) {
+            let i_end = i + literal.0.len();
+            return Some(i..i_end);
         }
-        if subslice.starts_with(tag.1) {
-            return Some(i);
+        if subslice.starts_with(literal.1) {
+            let i_end = i + literal.1.len();
+            return Some(i..i_end);
         }
-        if subslice.starts_with(tag.2) {
-            return Some(i);
+        if subslice.starts_with(literal.2) {
+            let i_end = i + literal.2.len();
+            return Some(i..i_end);
         }
     }
     None
 }
 
 #[cfg(not(feature = "simd"))]
-fn memmem_(slice: &[u8], tag: &[u8]) -> Option<usize> {
+fn memmem_(slice: &[u8], literal: &[u8]) -> Option<crate::lib::std::ops::Range<usize>> {
     for i in 0..slice.len() {
         let subslice = &slice[i..];
-        if subslice.starts_with(tag) {
-            return Some(i);
+        if subslice.starts_with(literal) {
+            let i_end = i + literal.len();
+            return Some(i..i_end);
         }
     }
     None
 }
 
 #[cfg(not(feature = "simd"))]
-fn memmem2_(slice: &[u8], tag: (&[u8], &[u8])) -> Option<usize> {
+fn memmem2_(slice: &[u8], literal: (&[u8], &[u8])) -> Option<crate::lib::std::ops::Range<usize>> {
     for i in 0..slice.len() {
         let subslice = &slice[i..];
-        if subslice.starts_with(tag.0) {
-            return Some(i);
+        if subslice.starts_with(literal.0) {
+            let i_end = i + literal.0.len();
+            return Some(i..i_end);
         }
-        if subslice.starts_with(tag.1) {
-            return Some(i);
+        if subslice.starts_with(literal.1) {
+            let i_end = i + literal.1.len();
+            return Some(i..i_end);
         }
     }
     None
 }
 
 #[cfg(not(feature = "simd"))]
-fn memmem3_(slice: &[u8], tag: (&[u8], &[u8], &[u8])) -> Option<usize> {
+fn memmem3_(
+    slice: &[u8],
+    literal: (&[u8], &[u8], &[u8]),
+) -> Option<crate::lib::std::ops::Range<usize>> {
     for i in 0..slice.len() {
         let subslice = &slice[i..];
-        if subslice.starts_with(tag.0) {
-            return Some(i);
+        if subslice.starts_with(literal.0) {
+            let i_end = i + literal.0.len();
+            return Some(i..i_end);
         }
-        if subslice.starts_with(tag.1) {
-            return Some(i);
+        if subslice.starts_with(literal.1) {
+            let i_end = i + literal.1.len();
+            return Some(i..i_end);
         }
-        if subslice.starts_with(tag.2) {
-            return Some(i);
+        if subslice.starts_with(literal.2) {
+            let i_end = i + literal.2.len();
+            return Some(i..i_end);
         }
     }
     None
diff --git a/crates/winnow/src/stream/tests.rs b/crates/winnow/src/stream/tests.rs
index 06047df..56fbc4c 100644
--- a/crates/winnow/src/stream/tests.rs
+++ b/crates/winnow/src/stream/tests.rs
@@ -3,7 +3,7 @@
 
 use crate::error::ErrMode::Backtrack;
 use crate::error::{ErrorKind, InputError};
-use crate::token::tag;
+use crate::token::literal;
 use crate::{
     combinator::{separated, separated_pair},
     PResult, Parser,
@@ -151,78 +151,78 @@
 }
 
 #[test]
-fn test_tag_support_char() {
+fn test_literal_support_char() {
     assert_eq!(
-        tag::<_, _, InputError<_>>('π').parse_peek("π"),
+        literal::<_, _, InputError<_>>('π').parse_peek("π"),
         Ok(("", "π"))
     );
     assert_eq!(
-        tag::<_, _, InputError<_>>('π').parse_peek("π3.14"),
+        literal::<_, _, InputError<_>>('π').parse_peek("π3.14"),
         Ok(("3.14", "π"))
     );
 
     assert_eq!(
-        tag::<_, _, InputError<_>>("π").parse_peek("π3.14"),
+        literal::<_, _, InputError<_>>("π").parse_peek("π3.14"),
         Ok(("3.14", "π"))
     );
 
     assert_eq!(
-        tag::<_, _, InputError<_>>('-').parse_peek("π"),
+        literal::<_, _, InputError<_>>('-').parse_peek("π"),
         Err(Backtrack(InputError::new("π", ErrorKind::Tag)))
     );
 
     assert_eq!(
-        tag::<_, Partial<&[u8]>, InputError<_>>('π').parse_peek(Partial::new(b"\xCF\x80")),
+        literal::<_, Partial<&[u8]>, InputError<_>>('π').parse_peek(Partial::new(b"\xCF\x80")),
         Ok((Partial::new(Default::default()), "π".as_bytes()))
     );
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x80"),
+        literal::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x80"),
         Ok((Default::default(), "π".as_bytes()))
     );
 
     assert_eq!(
-        tag::<_, Partial<&[u8]>, InputError<_>>('π').parse_peek(Partial::new(b"\xCF\x803.14")),
+        literal::<_, Partial<&[u8]>, InputError<_>>('π').parse_peek(Partial::new(b"\xCF\x803.14")),
         Ok((Partial::new(&b"3.14"[..]), "π".as_bytes()))
     );
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x80"),
+        literal::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x80"),
         Ok((Default::default(), "π".as_bytes()))
     );
 
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x803.14"),
+        literal::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x803.14"),
         Ok((&b"3.14"[..], "π".as_bytes()))
     );
 
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>(AsciiCaseless('a')).parse_peek(b"ABCxyz"),
+        literal::<_, &[u8], InputError<_>>(AsciiCaseless('a')).parse_peek(b"ABCxyz"),
         Ok((&b"BCxyz"[..], &b"A"[..]))
     );
 
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>('a').parse_peek(b"ABCxyz"),
+        literal::<_, &[u8], InputError<_>>('a').parse_peek(b"ABCxyz"),
         Err(Backtrack(InputError::new(&b"ABCxyz"[..], ErrorKind::Tag)))
     );
 
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>(AsciiCaseless('π')).parse_peek(b"\xCF\x803.14"),
+        literal::<_, &[u8], InputError<_>>(AsciiCaseless('π')).parse_peek(b"\xCF\x803.14"),
         Ok((&b"3.14"[..], "π".as_bytes()))
     );
 
     assert_eq!(
-        tag::<_, _, InputError<_>>(AsciiCaseless('🧑')).parse_peek("🧑你好"),
+        literal::<_, _, InputError<_>>(AsciiCaseless('🧑')).parse_peek("🧑你好"),
         Ok(("你好", "🧑"))
     );
 
     let mut buffer = [0; 4];
     let input = '\u{241b}'.encode_utf8(&mut buffer);
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>(AsciiCaseless('␛')).parse_peek(input.as_bytes()),
+        literal::<_, &[u8], InputError<_>>(AsciiCaseless('␛')).parse_peek(input.as_bytes()),
         Ok((&b""[..], [226, 144, 155].as_slice()))
     );
 
     assert_eq!(
-        tag::<_, &[u8], InputError<_>>('-').parse_peek(b"\xCF\x80"),
+        literal::<_, &[u8], InputError<_>>('-').parse_peek(b"\xCF\x80"),
         Err(Backtrack(InputError::new(&b"\xCF\x80"[..], ErrorKind::Tag)))
     );
 }
diff --git a/crates/winnow/src/token/mod.rs b/crates/winnow/src/token/mod.rs
index 8641515..f17ded0 100644
--- a/crates/winnow/src/token/mod.rs
+++ b/crates/winnow/src/token/mod.rs
@@ -4,6 +4,7 @@
 mod tests;
 
 use crate::combinator::trace;
+use crate::combinator::DisplayDebug;
 use crate::error::ErrMode;
 use crate::error::ErrorKind;
 use crate::error::Needed;
@@ -19,7 +20,18 @@
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn any(input: &mut &str) -> PResult<char>
+/// # {
+/// #     winnow::token::any.parse_next(input)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -43,13 +55,13 @@
 /// ```
 #[inline(always)]
 #[doc(alias = "token")]
-pub fn any<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Token, E>
+pub fn any<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Token, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
+    Input: StreamIsPartial + Stream,
+    Error: ParserError<Input>,
 {
-    trace("any", move |input: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() {
+    trace("any", move |input: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() {
             any_::<_, _, true>(input)
         } else {
             any_::<_, _, false>(input)
@@ -76,20 +88,35 @@
 
 /// Recognizes a literal
 ///
-/// The input data will be compared to the tag combinator's argument and will return the part of
+/// The input data will be compared to the literal combinator's argument and will return the part of
 /// the input that matches the argument
 ///
-/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern
+/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the literal
+///
+/// <div class="warning">
 ///
 /// **Note:** [`Parser`] is implemented for strings and byte strings as a convenience (complete
 /// only)
 ///
+/// </div>
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// # use winnow::error::ContextError;
+/// pub fn literal(literal: &str) -> impl Parser<&str, &str, ContextError>
+/// # {
+/// #     winnow::token::literal(literal)
+/// # }
+/// ```
+///
 /// # Example
 /// ```rust
 /// # use winnow::prelude::*;
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
-/// use winnow::token::tag;
-///
+/// #
 /// fn parser(s: &str) -> IResult<&str, &str> {
 ///   "Hello".parse_peek(s)
 /// }
@@ -103,7 +130,6 @@
 /// # use winnow::prelude::*;
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::Partial;
-/// use winnow::token::tag;
 ///
 /// fn parser(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
 ///   "Hello".parse_peek(s)
@@ -118,11 +144,11 @@
 /// ```rust
 /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
 /// # use winnow::prelude::*;
-/// use winnow::token::tag;
+/// use winnow::token::literal;
 /// use winnow::ascii::Caseless;
 ///
 /// fn parser(s: &str) -> IResult<&str, &str> {
-///   tag(Caseless("hello")).parse_peek(s)
+///   literal(Caseless("hello")).parse_peek(s)
 /// }
 ///
 /// assert_eq!(parser("Hello, World!"), Ok((", World!", "Hello")));
@@ -132,40 +158,42 @@
 /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
 /// ```
 #[inline(always)]
-#[doc(alias = "literal")]
+#[doc(alias = "tag")]
 #[doc(alias = "bytes")]
 #[doc(alias = "just")]
-pub fn tag<T, I, Error: ParserError<I>>(tag: T) -> impl Parser<I, <I as Stream>::Slice, Error>
+pub fn literal<Literal, Input, Error>(
+    literal: Literal,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream + Compare<T>,
-    T: SliceLen + Clone,
+    Input: StreamIsPartial + Stream + Compare<Literal>,
+    Literal: SliceLen + Clone + crate::lib::std::fmt::Debug,
+    Error: ParserError<Input>,
 {
-    trace("tag", move |i: &mut I| {
-        let t = tag.clone();
-        if <I as StreamIsPartial>::is_partial_supported() {
-            tag_::<_, _, _, true>(i, t)
+    trace(DisplayDebug(literal.clone()), move |i: &mut Input| {
+        let t = literal.clone();
+        if <Input as StreamIsPartial>::is_partial_supported() {
+            literal_::<_, _, _, true>(i, t)
         } else {
-            tag_::<_, _, _, false>(i, t)
+            literal_::<_, _, _, false>(i, t)
         }
     })
 }
 
-fn tag_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+fn literal_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
     i: &mut I,
     t: T,
 ) -> PResult<<I as Stream>::Slice, Error>
 where
     I: StreamIsPartial,
     I: Stream + Compare<T>,
-    T: SliceLen,
+    T: SliceLen + crate::lib::std::fmt::Debug,
 {
-    let tag_len = t.slice_len();
+    let literal_len = t.slice_len();
     match i.compare(t) {
-        CompareResult::Ok => Ok(i.next_slice(tag_len)),
-        CompareResult::Incomplete if PARTIAL && i.is_partial() => {
-            Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset())))
-        }
+        CompareResult::Ok(len) => Ok(i.next_slice(len)),
+        CompareResult::Incomplete if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(
+            Needed::new(literal_len - i.eof_offset()),
+        )),
         CompareResult::Incomplete | CompareResult::Error => {
             let e: ErrorKind = ErrorKind::Tag;
             Err(ErrMode::from_error_kind(i, e))
@@ -173,104 +201,33 @@
     }
 }
 
-/// Recognizes a case insensitive literal.
+/// Recognize a token that matches a [set of tokens][ContainsToken]
 ///
-/// The input data will be compared to the tag combinator's argument and will return the part of
-/// the input that matches the argument with no regard to case.
-///
-/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern.
-///
-/// # Example
-///
-/// ```rust
-/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
-/// # use winnow::prelude::*;
-/// use winnow::token::tag_no_case;
-///
-/// fn parser(s: &str) -> IResult<&str, &str> {
-///   tag_no_case("hello").parse_peek(s)
-/// }
-///
-/// assert_eq!(parser("Hello, World!"), Ok((", World!", "Hello")));
-/// assert_eq!(parser("hello, World!"), Ok((", World!", "hello")));
-/// assert_eq!(parser("HeLlO, World!"), Ok((", World!", "HeLlO")));
-/// assert_eq!(parser("Something"), Err(ErrMode::Backtrack(InputError::new("Something", ErrorKind::Tag))));
-/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
-/// ```
-///
-/// ```rust
-/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
-/// # use winnow::prelude::*;
-/// # use winnow::Partial;
-/// use winnow::token::tag_no_case;
-///
-/// fn parser(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
-///   tag_no_case("hello").parse_peek(s)
-/// }
-///
-/// assert_eq!(parser(Partial::new("Hello, World!")), Ok((Partial::new(", World!"), "Hello")));
-/// assert_eq!(parser(Partial::new("hello, World!")), Ok((Partial::new(", World!"), "hello")));
-/// assert_eq!(parser(Partial::new("HeLlO, World!")), Ok((Partial::new(", World!"), "HeLlO")));
-/// assert_eq!(parser(Partial::new("Something")), Err(ErrMode::Backtrack(InputError::new(Partial::new("Something"), ErrorKind::Tag))));
-/// assert_eq!(parser(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(5))));
-/// ```
-#[inline(always)]
-#[doc(alias = "literal")]
-#[doc(alias = "bytes")]
-#[doc(alias = "just")]
-#[deprecated(since = "0.5.20", note = "Replaced with `tag(ascii::Caseless(_))`")]
-pub fn tag_no_case<T, I, Error: ParserError<I>>(
-    tag: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream + Compare<T>,
-    T: SliceLen + Clone,
-{
-    trace("tag_no_case", move |i: &mut I| {
-        let t = tag.clone();
-        if <I as StreamIsPartial>::is_partial_supported() {
-            tag_no_case_::<_, _, _, true>(i, t)
-        } else {
-            tag_no_case_::<_, _, _, false>(i, t)
-        }
-    })
-}
-
-#[allow(deprecated)]
-fn tag_no_case_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
-    i: &mut I,
-    t: T,
-) -> PResult<<I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream + Compare<T>,
-    T: SliceLen,
-{
-    let tag_len = t.slice_len();
-
-    match i.compare_no_case(t) {
-        CompareResult::Ok => Ok(i.next_slice(tag_len)),
-        CompareResult::Incomplete if PARTIAL && i.is_partial() => {
-            Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset())))
-        }
-        CompareResult::Incomplete | CompareResult::Error => {
-            let e: ErrorKind = ErrorKind::Tag;
-            Err(ErrMode::from_error_kind(i, e))
-        }
-    }
-}
-
-/// Recognize a token that matches the [pattern][ContainsToken]
+/// <div class="warning">
 ///
 /// **Note:** [`Parser`] is implemented as a convenience (complete
 /// only) for
 /// - `u8`
 /// - `char`
 ///
+/// </div>
+///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// # use winnow::stream::ContainsToken;
+/// # use winnow::error::ContextError;
+/// pub fn one_of<'i>(set: impl ContainsToken<char>) -> impl Parser<&'i str, char, ContextError>
+/// # {
+/// #     winnow::token::one_of(set)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -310,24 +267,37 @@
 #[doc(alias = "char")]
 #[doc(alias = "token")]
 #[doc(alias = "satisfy")]
-pub fn one_of<I, T, Error: ParserError<I>>(list: T) -> impl Parser<I, <I as Stream>::Token, Error>
+pub fn one_of<Input, Set, Error>(set: Set) -> impl Parser<Input, <Input as Stream>::Token, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: Clone,
-    T: ContainsToken<<I as Stream>::Token>,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: Clone,
+    Set: ContainsToken<<Input as Stream>::Token>,
+    Error: ParserError<Input>,
 {
     trace(
         "one_of",
-        any.verify(move |t: &<I as Stream>::Token| list.contains_token(t.clone())),
+        any.verify(move |t: &<Input as Stream>::Token| set.contains_token(t.clone())),
     )
 }
 
-/// Recognize a token that does not match the [pattern][ContainsToken]
+/// Recognize a token that does not match a [set of tokens][ContainsToken]
 ///
 /// *Complete version*: Will return an error if there's not enough input data.
 ///
-/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+/// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// # use winnow::stream::ContainsToken;
+/// # use winnow::error::ContextError;
+/// pub fn none_of<'i>(set: impl ContainsToken<char>) -> impl Parser<&'i str, char, ContextError>
+/// # {
+/// #     winnow::token::none_of(set)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -350,27 +320,41 @@
 /// assert_eq!(none_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
 /// ```
 #[inline(always)]
-pub fn none_of<I, T, Error: ParserError<I>>(list: T) -> impl Parser<I, <I as Stream>::Token, Error>
+pub fn none_of<Input, Set, Error>(set: Set) -> impl Parser<Input, <Input as Stream>::Token, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    <I as Stream>::Token: Clone,
-    T: ContainsToken<<I as Stream>::Token>,
+    Input: StreamIsPartial + Stream,
+    <Input as Stream>::Token: Clone,
+    Set: ContainsToken<<Input as Stream>::Token>,
+    Error: ParserError<Input>,
 {
     trace(
         "none_of",
-        any.verify(move |t: &<I as Stream>::Token| !list.contains_token(t.clone())),
+        any.verify(move |t: &<Input as Stream>::Token| !set.contains_token(t.clone())),
     )
 }
 
-/// Recognize the longest (m <= len <= n) input slice that matches the [pattern][ContainsToken]
+/// Recognize the longest (m <= len <= n) input slice that matches a [set of tokens][ContainsToken]
 ///
-/// It will return an `ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice))` if the pattern wasn't met or is out
+/// It will return an `ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice))` if the set of tokens wasn't met or is out
 /// of range (m <= len <= n).
 ///
-/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the pattern reaches the end of the input or is too short.
+/// *[Partial version][crate::_topic::partial]* will return a `ErrMode::Incomplete(Needed::new(1))` if a member of the set of tokens reaches the end of the input or is too short.
 ///
-/// To recognize a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::recognize`].
+/// To take a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::take`].
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` [ranges][Range]:
+/// ```rust
+/// # use std::ops::RangeFrom;
+/// # use winnow::prelude::*;
+/// # use winnow::stream::ContainsToken;
+/// # use winnow::error::ContextError;
+/// pub fn take_while<'i>(occurrences: RangeFrom<usize>, set: impl ContainsToken<char>) -> impl Parser<&'i str, &'i str, ContextError>
+/// # {
+/// #     winnow::token::take_while(occurrences, set)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -499,117 +483,65 @@
 #[doc(alias = "is_a")]
 #[doc(alias = "take_while0")]
 #[doc(alias = "take_while1")]
-pub fn take_while<T, I, Error: ParserError<I>>(
-    range: impl Into<Range>,
-    list: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
+pub fn take_while<Set, Input, Error>(
+    occurrences: impl Into<Range>,
+    set: Set,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
+    Input: StreamIsPartial + Stream,
+    Set: ContainsToken<<Input as Stream>::Token>,
+    Error: ParserError<Input>,
 {
     let Range {
         start_inclusive,
         end_inclusive,
-    } = range.into();
-    trace("take_while", move |i: &mut I| {
+    } = occurrences.into();
+    trace("take_while", move |i: &mut Input| {
         match (start_inclusive, end_inclusive) {
             (0, None) => {
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_while0_::<_, _, _, true>(i, &list)
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_till0::<_, _, _, true>(i, |c| !set.contains_token(c))
                 } else {
-                    take_while0_::<_, _, _, false>(i, &list)
+                    take_till0::<_, _, _, false>(i, |c| !set.contains_token(c))
                 }
             }
             (1, None) => {
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_while1_::<_, _, _, true>(i, &list)
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_till1::<_, _, _, true>(i, |c| !set.contains_token(c))
                 } else {
-                    take_while1_::<_, _, _, false>(i, &list)
+                    take_till1::<_, _, _, false>(i, |c| !set.contains_token(c))
                 }
             }
             (start, end) => {
                 let end = end.unwrap_or(usize::MAX);
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_while_m_n_::<_, _, _, true>(i, start, end, &list)
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_till_m_n::<_, _, _, true>(i, start, end, |c| !set.contains_token(c))
                 } else {
-                    take_while_m_n_::<_, _, _, false>(i, start, end, &list)
+                    take_till_m_n::<_, _, _, false>(i, start, end, |c| !set.contains_token(c))
                 }
             }
         }
     })
 }
 
-fn take_while0_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
-    input: &mut I,
-    list: &T,
-) -> PResult<<I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
-{
-    if PARTIAL && input.is_partial() {
-        take_till0_partial(input, |c| !list.contains_token(c))
-    } else {
-        take_till0_complete(input, |c| !list.contains_token(c))
-    }
-}
-
-fn take_while1_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
-    input: &mut I,
-    list: &T,
-) -> PResult<<I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
-{
-    if PARTIAL && input.is_partial() {
-        take_till1_partial(input, |c| !list.contains_token(c))
-    } else {
-        take_till1_complete(input, |c| !list.contains_token(c))
-    }
-}
-
-fn take_while_m_n_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
-    input: &mut I,
-    m: usize,
-    n: usize,
-    list: &T,
-) -> PResult<<I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
-{
-    take_till_m_n::<_, _, _, PARTIAL>(input, m, n, |c| !list.contains_token(c))
-}
-
-/// Looks for the first element of the input type for which the condition returns true,
-/// and returns the input up to this position.
-///
-/// *Partial version*: If no element is found matching the condition, this will return `Incomplete`
-fn take_till0_partial<P, I: Stream, E: ParserError<I>>(
+fn take_till0<P, I: StreamIsPartial + Stream, E: ParserError<I>, const PARTIAL: bool>(
     input: &mut I,
     predicate: P,
 ) -> PResult<<I as Stream>::Slice, E>
 where
     P: Fn(I::Token) -> bool,
 {
-    let offset = input
-        .offset_for(predicate)
-        .ok_or_else(|| ErrMode::Incomplete(Needed::new(1)))?;
+    let offset = match input.offset_for(predicate) {
+        Some(offset) => offset,
+        None if PARTIAL && input.is_partial() => {
+            return Err(ErrMode::Incomplete(Needed::new(1)));
+        }
+        None => input.eof_offset(),
+    };
     Ok(input.next_slice(offset))
 }
 
-/// Looks for the first element of the input type for which the condition returns true
-/// and returns the input up to this position.
-///
-/// Fails if the produced slice is empty.
-///
-/// *Partial version*: If no element is found matching the condition, this will return `Incomplete`
-fn take_till1_partial<P, I: Stream, E: ParserError<I>>(
+fn take_till1<P, I: StreamIsPartial + Stream, E: ParserError<I>, const PARTIAL: bool>(
     input: &mut I,
     predicate: P,
 ) -> PResult<<I as Stream>::Slice, E>
@@ -617,50 +549,13 @@
     P: Fn(I::Token) -> bool,
 {
     let e: ErrorKind = ErrorKind::Slice;
-    let offset = input
-        .offset_for(predicate)
-        .ok_or_else(|| ErrMode::Incomplete(Needed::new(1)))?;
-    if offset == 0 {
-        Err(ErrMode::from_error_kind(input, e))
-    } else {
-        Ok(input.next_slice(offset))
-    }
-}
-
-/// Looks for the first element of the input type for which the condition returns true,
-/// and returns the input up to this position.
-///
-/// *Complete version*: If no element is found matching the condition, this will return the whole input
-fn take_till0_complete<P, I: Stream, E: ParserError<I>>(
-    input: &mut I,
-    predicate: P,
-) -> PResult<<I as Stream>::Slice, E>
-where
-    P: Fn(I::Token) -> bool,
-{
-    let offset = input
-        .offset_for(predicate)
-        .unwrap_or_else(|| input.eof_offset());
-    Ok(input.next_slice(offset))
-}
-
-/// Looks for the first element of the input type for which the condition returns true
-/// and returns the input up to this position.
-///
-/// Fails if the produced slice is empty.
-///
-/// *Complete version*: If no element is found matching the condition, this will return the whole input
-fn take_till1_complete<P, I: Stream, E: ParserError<I>>(
-    input: &mut I,
-    predicate: P,
-) -> PResult<<I as Stream>::Slice, E>
-where
-    P: Fn(I::Token) -> bool,
-{
-    let e: ErrorKind = ErrorKind::Slice;
-    let offset = input
-        .offset_for(predicate)
-        .unwrap_or_else(|| input.eof_offset());
+    let offset = match input.offset_for(predicate) {
+        Some(offset) => offset,
+        None if PARTIAL && input.is_partial() => {
+            return Err(ErrMode::Incomplete(Needed::new(1)));
+        }
+        None => input.eof_offset(),
+    };
     if offset == 0 {
         Err(ErrMode::from_error_kind(input, e))
     } else {
@@ -680,7 +575,10 @@
     P: Fn(I::Token) -> bool,
 {
     if n < m {
-        return Err(ErrMode::assert(input, "`m` should be <= `n`"));
+        return Err(ErrMode::assert(
+            input,
+            "`occurrences` should be ascending, rather than descending",
+        ));
     }
 
     let mut final_count = 0;
@@ -718,11 +616,31 @@
     }
 }
 
-/// Recognize the longest input slice (if any) till a [pattern][ContainsToken] is met.
+/// Recognize the longest input slice (if any) till a member of a [set of tokens][ContainsToken] is found.
 ///
-/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the
+/// It doesn't consume the terminating token from the set.
+///
+/// *[Partial version][crate::_topic::partial]* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the
 /// end of input or if there was not match.
 ///
+/// See also
+/// - [`take_until`] for recognizing up-to a [`literal`] (w/ optional simd optimizations)
+/// - [`repeat_till`][crate::combinator::repeat_till] with [`Parser::take`] for taking tokens up to a [`Parser`]
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` [ranges][Range]:
+/// ```rust
+/// # use std::ops::RangeFrom;
+/// # use winnow::prelude::*;
+/// # use winnow::stream::ContainsToken;
+/// # use winnow::error::ContextError;
+/// pub fn take_till<'i>(occurrences: RangeFrom<usize>, set: impl ContainsToken<char>) -> impl Parser<&'i str, &'i str, ContextError>
+/// # {
+/// #     winnow::token::take_till(occurrences, set)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```rust
@@ -757,191 +675,72 @@
 /// ```
 #[inline(always)]
 #[doc(alias = "is_not")]
-pub fn take_till<T, I, Error: ParserError<I>>(
-    range: impl Into<Range>,
-    list: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
+pub fn take_till<Set, Input, Error>(
+    occurrences: impl Into<Range>,
+    set: Set,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
+    Input: StreamIsPartial + Stream,
+    Set: ContainsToken<<Input as Stream>::Token>,
+    Error: ParserError<Input>,
 {
     let Range {
         start_inclusive,
         end_inclusive,
-    } = range.into();
-    trace("take_till", move |i: &mut I| {
+    } = occurrences.into();
+    trace("take_till", move |i: &mut Input| {
         match (start_inclusive, end_inclusive) {
             (0, None) => {
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_till0_partial(i, |c| list.contains_token(c))
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_till0::<_, _, _, true>(i, |c| set.contains_token(c))
                 } else {
-                    take_till0_complete(i, |c| list.contains_token(c))
+                    take_till0::<_, _, _, false>(i, |c| set.contains_token(c))
                 }
             }
             (1, None) => {
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_till1_partial(i, |c| list.contains_token(c))
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_till1::<_, _, _, true>(i, |c| set.contains_token(c))
                 } else {
-                    take_till1_complete(i, |c| list.contains_token(c))
+                    take_till1::<_, _, _, false>(i, |c| set.contains_token(c))
                 }
             }
             (start, end) => {
                 let end = end.unwrap_or(usize::MAX);
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_till_m_n::<_, _, _, true>(i, start, end, |c| list.contains_token(c))
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_till_m_n::<_, _, _, true>(i, start, end, |c| set.contains_token(c))
                 } else {
-                    take_till_m_n::<_, _, _, false>(i, start, end, |c| list.contains_token(c))
+                    take_till_m_n::<_, _, _, false>(i, start, end, |c| set.contains_token(c))
                 }
             }
         }
     })
 }
 
-/// Recognize the longest input slice (if any) till a [pattern][ContainsToken] is met.
-///
-/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the
-/// end of input or if there was not match.
-///
-/// # Example
-///
-/// ```rust
-/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
-/// # use winnow::prelude::*;
-/// use winnow::token::take_till0;
-///
-/// fn till_colon(s: &str) -> IResult<&str, &str> {
-///   take_till0(|c| c == ':').parse_peek(s)
-/// }
-///
-/// assert_eq!(till_colon("latin:123"), Ok((":123", "latin")));
-/// assert_eq!(till_colon(":empty matched"), Ok((":empty matched", ""))); //allowed
-/// assert_eq!(till_colon("12345"), Ok(("", "12345")));
-/// assert_eq!(till_colon(""), Ok(("", "")));
-/// ```
-///
-/// ```rust
-/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
-/// # use winnow::prelude::*;
-/// # use winnow::Partial;
-/// use winnow::token::take_till0;
-///
-/// fn till_colon(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
-///   take_till0(|c| c == ':').parse_peek(s)
-/// }
-///
-/// assert_eq!(till_colon(Partial::new("latin:123")), Ok((Partial::new(":123"), "latin")));
-/// assert_eq!(till_colon(Partial::new(":empty matched")), Ok((Partial::new(":empty matched"), ""))); //allowed
-/// assert_eq!(till_colon(Partial::new("12345")), Err(ErrMode::Incomplete(Needed::new(1))));
-/// assert_eq!(till_colon(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
-/// ```
-#[deprecated(since = "0.5.21", note = "Replaced with `take_till(0.., ...)`")]
-#[inline(always)]
-pub fn take_till0<T, I, Error: ParserError<I>>(
-    list: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
-{
-    trace("take_till0", move |i: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() {
-            take_till0_partial(i, |c| list.contains_token(c))
-        } else {
-            take_till0_complete(i, |c| list.contains_token(c))
-        }
-    })
-}
-
-/// Recognize the longest (at least 1) input slice till a [pattern][ContainsToken] is met.
-///
-/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is empty or the
-/// predicate matches the first input.
-///
-/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the
-/// end of input or if there was not match.
-///
-/// # Example
-///
-/// ```rust
-/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
-/// # use winnow::prelude::*;
-/// use winnow::token::take_till1;
-///
-/// fn till_colon(s: &str) -> IResult<&str, &str> {
-///   take_till1(|c| c == ':').parse_peek(s)
-/// }
-///
-/// assert_eq!(till_colon("latin:123"), Ok((":123", "latin")));
-/// assert_eq!(till_colon(":empty matched"), Err(ErrMode::Backtrack(InputError::new(":empty matched", ErrorKind::Slice))));
-/// assert_eq!(till_colon("12345"), Ok(("", "12345")));
-/// assert_eq!(till_colon(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
-///
-/// fn not_space(s: &str) -> IResult<&str, &str> {
-///   take_till1([' ', '\t', '\r', '\n']).parse_peek(s)
-/// }
-///
-/// assert_eq!(not_space("Hello, World!"), Ok((" World!", "Hello,")));
-/// assert_eq!(not_space("Sometimes\t"), Ok(("\t", "Sometimes")));
-/// assert_eq!(not_space("Nospace"), Ok(("", "Nospace")));
-/// assert_eq!(not_space(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
-/// ```
-///
-/// ```rust
-/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
-/// # use winnow::prelude::*;
-/// # use winnow::Partial;
-/// use winnow::token::take_till1;
-///
-/// fn till_colon(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
-///   take_till1(|c| c == ':').parse_peek(s)
-/// }
-///
-/// assert_eq!(till_colon(Partial::new("latin:123")), Ok((Partial::new(":123"), "latin")));
-/// assert_eq!(till_colon(Partial::new(":empty matched")), Err(ErrMode::Backtrack(InputError::new(Partial::new(":empty matched"), ErrorKind::Slice))));
-/// assert_eq!(till_colon(Partial::new("12345")), Err(ErrMode::Incomplete(Needed::new(1))));
-/// assert_eq!(till_colon(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
-///
-/// fn not_space(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
-///   take_till1([' ', '\t', '\r', '\n']).parse_peek(s)
-/// }
-///
-/// assert_eq!(not_space(Partial::new("Hello, World!")), Ok((Partial::new(" World!"), "Hello,")));
-/// assert_eq!(not_space(Partial::new("Sometimes\t")), Ok((Partial::new("\t"), "Sometimes")));
-/// assert_eq!(not_space(Partial::new("Nospace")), Err(ErrMode::Incomplete(Needed::new(1))));
-/// assert_eq!(not_space(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
-/// ```
-#[inline(always)]
-#[deprecated(since = "0.5.21", note = "Replaced with `take_till(1.., ...)`")]
-pub fn take_till1<T, I, Error: ParserError<I>>(
-    list: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream,
-    T: ContainsToken<<I as Stream>::Token>,
-{
-    trace("take_till1", move |i: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() {
-            take_till1_partial(i, |c| list.contains_token(c))
-        } else {
-            take_till1_complete(i, |c| list.contains_token(c))
-        }
-    })
-}
-
 /// Recognize an input slice containing the first N input elements (I[..N]).
 ///
 /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is shorter than the argument.
 ///
-/// *Partial version*: if the input has less than N elements, `take` will
+/// *[Partial version][crate::_topic::partial]*: if the input has less than N elements, `take` will
 /// return a `ErrMode::Incomplete(Needed::new(M))` where M is the number of
 /// additional bytes the parser would need to succeed.
 /// It is well defined for `&[u8]` as the number of elements is the byte size,
 /// but for types like `&str`, we cannot know how many bytes correspond for
 /// the next few chars, so the result will be `ErrMode::Incomplete(Needed::Unknown)`
 ///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` ranges:
+/// ```rust
+/// # use std::ops::RangeFrom;
+/// # use winnow::prelude::*;
+/// # use winnow::stream::ContainsToken;
+/// # use winnow::error::ContextError;
+/// pub fn take<'i>(token_count: usize) -> impl Parser<&'i str, &'i str, ContextError>
+/// # {
+/// #     winnow::token::take(token_count)
+/// # }
+/// ```
+///
 /// # Example
 ///
 /// ```rust
@@ -988,15 +787,17 @@
 /// assert_eq!(take6(Partial::new("short")), Err(ErrMode::Incomplete(Needed::Unknown)));
 /// ```
 #[inline(always)]
-pub fn take<C, I, Error: ParserError<I>>(count: C) -> impl Parser<I, <I as Stream>::Slice, Error>
+pub fn take<UsizeLike, Input, Error>(
+    token_count: UsizeLike,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream,
-    C: ToUsize,
+    Input: StreamIsPartial + Stream,
+    UsizeLike: ToUsize,
+    Error: ParserError<Input>,
 {
-    let c = count.to_usize();
-    trace("take", move |i: &mut I| {
-        if <I as StreamIsPartial>::is_partial_supported() {
+    let c = token_count.to_usize();
+    trace("take", move |i: &mut Input| {
+        if <Input as StreamIsPartial>::is_partial_supported() {
             take_::<_, _, true>(i, c)
         } else {
             take_::<_, _, false>(i, c)
@@ -1019,15 +820,34 @@
     }
 }
 
-/// Recognize the input slice up to the first occurrence of the literal.
+/// Recognize the input slice up to the first occurrence of a [literal].
 ///
-/// It doesn't consume the pattern.
+/// Feature `simd` will enable the use of [`memchr`](https://docs.rs/memchr/latest/memchr/).
+///
+/// It doesn't consume the literal.
 ///
 /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))`
-/// if the pattern wasn't met.
+/// if the literal wasn't met.
 ///
-/// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't
-/// contain the pattern or if the input is smaller than the pattern.
+/// *[Partial version][crate::_topic::partial]*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't
+/// contain the literal or if the input is smaller than the literal.
+///
+/// See also
+/// - [`take_till`] for recognizing up-to a [set of tokens][ContainsToken]
+/// - [`repeat_till`][crate::combinator::repeat_till] with [`Parser::take`] for taking tokens up to a [`Parser`]
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` [ranges][Range]:
+/// ```rust
+/// # use std::ops::RangeFrom;
+/// # use winnow::prelude::*;;
+/// # use winnow::error::ContextError;
+/// pub fn take_until(occurrences: RangeFrom<usize>, literal: &str) -> impl Parser<&str, &str, ContextError>
+/// # {
+/// #     winnow::token::take_until(occurrences, literal)
+/// # }
+/// ```
 ///
 /// # Example
 ///
@@ -1095,60 +915,47 @@
 /// assert_eq!(until_eof(Partial::new("eof")), Err(ErrMode::Backtrack(InputError::new(Partial::new("eof"), ErrorKind::Slice))));
 /// ```
 #[inline(always)]
-pub fn take_until<T, I, Error: ParserError<I>>(
-    range: impl Into<Range>,
-    tag: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
+pub fn take_until<Literal, Input, Error>(
+    occurrences: impl Into<Range>,
+    literal: Literal,
+) -> impl Parser<Input, <Input as Stream>::Slice, Error>
 where
-    I: StreamIsPartial,
-    I: Stream + FindSlice<T>,
-    T: SliceLen + Clone,
+    Input: StreamIsPartial + Stream + FindSlice<Literal>,
+    Literal: Clone,
+    Error: ParserError<Input>,
 {
     let Range {
         start_inclusive,
         end_inclusive,
-    } = range.into();
-    trace("take_until", move |i: &mut I| {
+    } = occurrences.into();
+    trace("take_until", move |i: &mut Input| {
         match (start_inclusive, end_inclusive) {
             (0, None) => {
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_until0_::<_, _, _, true>(i, tag.clone())
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_until0_::<_, _, _, true>(i, literal.clone())
                 } else {
-                    take_until0_::<_, _, _, false>(i, tag.clone())
+                    take_until0_::<_, _, _, false>(i, literal.clone())
                 }
             }
             (1, None) => {
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_until1_::<_, _, _, true>(i, tag.clone())
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_until1_::<_, _, _, true>(i, literal.clone())
                 } else {
-                    take_until1_::<_, _, _, false>(i, tag.clone())
+                    take_until1_::<_, _, _, false>(i, literal.clone())
                 }
             }
             (start, end) => {
                 let end = end.unwrap_or(usize::MAX);
-                if <I as StreamIsPartial>::is_partial_supported() {
-                    take_until_m_n_::<_, _, _, true>(i, start, end, tag.clone())
+                if <Input as StreamIsPartial>::is_partial_supported() {
+                    take_until_m_n_::<_, _, _, true>(i, start, end, literal.clone())
                 } else {
-                    take_until_m_n_::<_, _, _, false>(i, start, end, tag.clone())
+                    take_until_m_n_::<_, _, _, false>(i, start, end, literal.clone())
                 }
             }
         }
     })
 }
 
-/// Deprecated, see [`take_until`]
-#[deprecated(since = "0.5.35", note = "Replaced with `take_until`")]
-pub fn take_until0<T, I, Error: ParserError<I>>(
-    tag: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream + FindSlice<T>,
-    T: SliceLen + Clone,
-{
-    take_until(0.., tag)
-}
-
 fn take_until0_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
     i: &mut I,
     t: T,
@@ -1156,29 +963,14 @@
 where
     I: StreamIsPartial,
     I: Stream + FindSlice<T>,
-    T: SliceLen,
 {
     match i.find_slice(t) {
-        Some(offset) => Ok(i.next_slice(offset)),
+        Some(range) => Ok(i.next_slice(range.start)),
         None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)),
         None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
     }
 }
 
-/// Deprecated, see [`take_until`]
-#[deprecated(since = "0.5.35", note = "Replaced with `take_until`")]
-#[inline(always)]
-pub fn take_until1<T, I, Error: ParserError<I>>(
-    tag: T,
-) -> impl Parser<I, <I as Stream>::Slice, Error>
-where
-    I: StreamIsPartial,
-    I: Stream + FindSlice<T>,
-    T: SliceLen + Clone,
-{
-    take_until(1.., tag)
-}
-
 fn take_until1_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
     i: &mut I,
     t: T,
@@ -1186,12 +978,17 @@
 where
     I: StreamIsPartial,
     I: Stream + FindSlice<T>,
-    T: SliceLen,
 {
     match i.find_slice(t) {
         None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)),
-        None | Some(0) => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
-        Some(offset) => Ok(i.next_slice(offset)),
+        None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
+        Some(range) => {
+            if range.start == 0 {
+                Err(ErrMode::from_error_kind(i, ErrorKind::Slice))
+            } else {
+                Ok(i.next_slice(range.start))
+            }
+        }
     }
 }
 
@@ -1204,29 +1001,105 @@
 where
     I: StreamIsPartial,
     I: Stream + FindSlice<T>,
-    T: SliceLen,
 {
     if end < start {
-        return Err(ErrMode::assert(i, "`start` should be <= `end`"));
+        return Err(ErrMode::assert(
+            i,
+            "`occurrences` should be ascending, rather than descending",
+        ));
     }
 
     match i.find_slice(t) {
-        Some(offset) => {
+        Some(range) => {
             let start_offset = i.offset_at(start);
             let end_offset = i.offset_at(end).unwrap_or_else(|_err| i.eof_offset());
-            if start_offset.map(|s| offset < s).unwrap_or(true) {
+            if start_offset.map(|s| range.start < s).unwrap_or(true) {
                 if PARTIAL && i.is_partial() {
                     return Err(ErrMode::Incomplete(Needed::Unknown));
                 } else {
                     return Err(ErrMode::from_error_kind(i, ErrorKind::Slice));
                 }
             }
-            if end_offset < offset {
+            if end_offset < range.start {
                 return Err(ErrMode::from_error_kind(i, ErrorKind::Slice));
             }
-            Ok(i.next_slice(offset))
+            Ok(i.next_slice(range.start))
         }
         None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)),
         None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
     }
 }
+
+/// Return the remaining input.
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn rest<'i>(input: &mut &'i str) -> PResult<&'i str>
+/// # {
+/// #     winnow::token::rest.parse_next(input)
+/// # }
+/// ```
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::error::ErrorKind;
+/// # use winnow::error::InputError;
+/// use winnow::token::rest;
+/// assert_eq!(rest::<_,InputError<_>>.parse_peek("abc"), Ok(("", "abc")));
+/// assert_eq!(rest::<_,InputError<_>>.parse_peek(""), Ok(("", "")));
+/// ```
+#[inline]
+pub fn rest<Input, Error>(input: &mut Input) -> PResult<<Input as Stream>::Slice, Error>
+where
+    Input: Stream,
+    Error: ParserError<Input>,
+{
+    trace("rest", move |input: &mut Input| Ok(input.finish())).parse_next(input)
+}
+
+/// Return the length of the remaining input.
+///
+/// <div class="warning">
+///
+/// Note: this does not advance the [`Stream`]
+///
+/// </div>
+///
+/// # Effective Signature
+///
+/// Assuming you are parsing a `&str` [Stream]:
+/// ```rust
+/// # use winnow::prelude::*;;
+/// pub fn rest_len(input: &mut &str) -> PResult<usize>
+/// # {
+/// #     winnow::token::rest_len.parse_next(input)
+/// # }
+/// ```
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::error::ErrorKind;
+/// # use winnow::error::InputError;
+/// use winnow::token::rest_len;
+/// assert_eq!(rest_len::<_,InputError<_>>.parse_peek("abc"), Ok(("abc", 3)));
+/// assert_eq!(rest_len::<_,InputError<_>>.parse_peek(""), Ok(("", 0)));
+/// ```
+#[inline]
+pub fn rest_len<Input, Error>(input: &mut Input) -> PResult<usize, Error>
+where
+    Input: Stream,
+    Error: ParserError<Input>,
+{
+    trace("rest_len", move |input: &mut Input| {
+        let len = input.eof_offset();
+        Ok(len)
+    })
+    .parse_next(input)
+}
diff --git a/crates/winnow/src/token/tests.rs b/crates/winnow/src/token/tests.rs
index e8198f9..e00dca4 100644
--- a/crates/winnow/src/token/tests.rs
+++ b/crates/winnow/src/token/tests.rs
@@ -10,12 +10,19 @@
 use crate::error::InputError;
 use crate::error::Needed;
 use crate::stream::AsChar;
-use crate::token::tag;
+use crate::token::literal;
 use crate::unpeek;
 use crate::IResult;
 use crate::Parser;
 use crate::Partial;
 
+macro_rules! assert_parse(
+  ($left: expr, $right: expr) => {
+    let res: $crate::IResult<_, _, InputError<_>> = $left;
+    assert_eq!(res, $right);
+  };
+);
+
 #[test]
 fn complete_take_while_m_n_utf8_all_matching() {
     let result: IResult<&str, &str> =
@@ -98,6 +105,137 @@
 }
 
 #[test]
+fn complete_take_until_empty() {
+    fn take_until_empty(i: &str) -> IResult<&str, &str> {
+        take_until(0, "").parse_peek(i)
+    }
+    assert_eq!(take_until_empty(""), Ok(("", "")));
+    assert_eq!(take_until_empty("end"), Ok(("end", "")));
+}
+
+#[test]
+fn complete_literal_case_insensitive() {
+    fn caseless_bytes(i: &[u8]) -> IResult<&[u8], &[u8]> {
+        literal(Caseless("ABcd")).parse_peek(i)
+    }
+    assert_eq!(
+        caseless_bytes(&b"aBCdefgh"[..]),
+        Ok((&b"efgh"[..], &b"aBCd"[..]))
+    );
+    assert_eq!(
+        caseless_bytes(&b"abcdefgh"[..]),
+        Ok((&b"efgh"[..], &b"abcd"[..]))
+    );
+    assert_eq!(
+        caseless_bytes(&b"ABCDefgh"[..]),
+        Ok((&b"efgh"[..], &b"ABCD"[..]))
+    );
+    assert_eq!(
+        caseless_bytes(&b"ab"[..]),
+        Err(ErrMode::Backtrack(error_position!(
+            &&b"ab"[..],
+            ErrorKind::Tag
+        )))
+    );
+    assert_eq!(
+        caseless_bytes(&b"Hello"[..]),
+        Err(ErrMode::Backtrack(error_position!(
+            &&b"Hello"[..],
+            ErrorKind::Tag
+        )))
+    );
+    assert_eq!(
+        caseless_bytes(&b"Hel"[..]),
+        Err(ErrMode::Backtrack(error_position!(
+            &&b"Hel"[..],
+            ErrorKind::Tag
+        )))
+    );
+
+    fn caseless_str(i: &str) -> IResult<&str, &str> {
+        literal(Caseless("ABcd")).parse_peek(i)
+    }
+    assert_eq!(caseless_str("aBCdefgh"), Ok(("efgh", "aBCd")));
+    assert_eq!(caseless_str("abcdefgh"), Ok(("efgh", "abcd")));
+    assert_eq!(caseless_str("ABCDefgh"), Ok(("efgh", "ABCD")));
+    assert_eq!(
+        caseless_str("ab"),
+        Err(ErrMode::Backtrack(error_position!(&"ab", ErrorKind::Tag)))
+    );
+    assert_eq!(
+        caseless_str("Hello"),
+        Err(ErrMode::Backtrack(error_position!(
+            &"Hello",
+            ErrorKind::Tag
+        )))
+    );
+    assert_eq!(
+        caseless_str("Hel"),
+        Err(ErrMode::Backtrack(error_position!(&"Hel", ErrorKind::Tag)))
+    );
+
+    fn matches_kelvin(i: &str) -> IResult<&str, &str> {
+        literal(Caseless("k")).parse_peek(i)
+    }
+    assert_eq!(
+        matches_kelvin("K"),
+        Err(ErrMode::Backtrack(error_position!(&"K", ErrorKind::Tag)))
+    );
+
+    fn is_kelvin(i: &str) -> IResult<&str, &str> {
+        literal(Caseless("K")).parse_peek(i)
+    }
+    assert_eq!(
+        is_kelvin("k"),
+        Err(ErrMode::Backtrack(error_position!(&"k", ErrorKind::Tag)))
+    );
+}
+
+#[test]
+fn complete_literal_fixed_size_array() {
+    fn test(i: &[u8]) -> IResult<&[u8], &[u8]> {
+        literal([0x42]).parse_peek(i)
+    }
+    fn test2(i: &[u8]) -> IResult<&[u8], &[u8]> {
+        literal(&[0x42]).parse_peek(i)
+    }
+
+    let input = &[0x42, 0x00][..];
+    assert_eq!(test(input), Ok((&b"\x00"[..], &b"\x42"[..])));
+    assert_eq!(test2(input), Ok((&b"\x00"[..], &b"\x42"[..])));
+}
+
+#[test]
+fn complete_literal_char() {
+    fn test(i: &[u8]) -> IResult<&[u8], &[u8]> {
+        literal('B').parse_peek(i)
+    }
+    assert_eq!(test(&[0x42, 0x00][..]), Ok((&b"\x00"[..], &b"\x42"[..])));
+    assert_eq!(
+        test(&[b'A', b'\0'][..]),
+        Err(ErrMode::Backtrack(error_position!(
+            &&b"A\0"[..],
+            ErrorKind::Tag
+        )))
+    );
+}
+
+#[test]
+fn complete_literal_byte() {
+    fn test(i: &[u8]) -> IResult<&[u8], &[u8]> {
+        literal(b'B').parse_peek(i)
+    }
+    assert_eq!(test(&[0x42, 0x00][..]), Ok((&b"\x00"[..], &b"\x42"[..])));
+    assert_eq!(
+        test(&[b'A', b'\0'][..]),
+        Err(ErrMode::Backtrack(error_position!(
+            &&b"A\0"[..],
+            ErrorKind::Tag
+        )))
+    );
+}
+
+#[test]
 fn partial_any_str() {
     use super::any;
     assert_eq!(
@@ -134,7 +272,7 @@
 
 #[test]
 fn char_byteslice() {
-    fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+    fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, char> {
         'c'.parse_peek(i)
     }
 
@@ -143,12 +281,12 @@
         f(Partial::new(a)),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new(a),
-            ErrorKind::Verify
+            ErrorKind::Tag
         )))
     );
 
     let b = &b"cde"[..];
-    assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), b'c')));
+    assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), 'c')));
 }
 
 #[test]
@@ -162,7 +300,7 @@
         f(Partial::new(a)),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new(a),
-            ErrorKind::Verify
+            ErrorKind::Tag
         )))
     );
 
@@ -267,16 +405,14 @@
 }
 
 #[test]
-fn partial_recognize() {
+fn partial_take() {
     use crate::ascii::{
         alpha1 as alpha, alphanumeric1 as alphanumeric, digit1 as digit, hex_digit1 as hex_digit,
         multispace1 as multispace, oct_digit1 as oct_digit, space1 as space,
     };
 
     fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        delimited("<!--", take(5_usize), "-->")
-            .recognize()
-            .parse_peek(i)
+        delimited("<!--", take(5_usize), "-->").take().parse_peek(i)
     }
     let r = x(Partial::new(&b"<!-- abc --> aaa"[..]));
     assert_eq!(r, Ok((Partial::new(&b" aaa"[..]), &b"<!-- abc -->"[..])));
@@ -284,43 +420,43 @@
     let semicolon = &b";"[..];
 
     fn ya(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        alpha.recognize().parse_peek(i)
+        alpha.take().parse_peek(i)
     }
     let ra = ya(Partial::new(&b"abc;"[..]));
     assert_eq!(ra, Ok((Partial::new(semicolon), &b"abc"[..])));
 
     fn yd(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        digit.recognize().parse_peek(i)
+        digit.take().parse_peek(i)
     }
     let rd = yd(Partial::new(&b"123;"[..]));
     assert_eq!(rd, Ok((Partial::new(semicolon), &b"123"[..])));
 
     fn yhd(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        hex_digit.recognize().parse_peek(i)
+        hex_digit.take().parse_peek(i)
     }
     let rhd = yhd(Partial::new(&b"123abcDEF;"[..]));
     assert_eq!(rhd, Ok((Partial::new(semicolon), &b"123abcDEF"[..])));
 
     fn yod(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        oct_digit.recognize().parse_peek(i)
+        oct_digit.take().parse_peek(i)
     }
     let rod = yod(Partial::new(&b"1234567;"[..]));
     assert_eq!(rod, Ok((Partial::new(semicolon), &b"1234567"[..])));
 
     fn yan(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        alphanumeric.recognize().parse_peek(i)
+        alphanumeric.take().parse_peek(i)
     }
     let ran = yan(Partial::new(&b"123abc;"[..]));
     assert_eq!(ran, Ok((Partial::new(semicolon), &b"123abc"[..])));
 
     fn ys(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        space.recognize().parse_peek(i)
+        space.take().parse_peek(i)
     }
     let rs = ys(Partial::new(&b" \t;"[..]));
     assert_eq!(rs, Ok((Partial::new(semicolon), &b" \t"[..])));
 
     fn yms(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        multispace.recognize().parse_peek(i)
+        multispace.take().parse_peek(i)
     }
     let rms = yms(Partial::new(&b" \t\r\n;"[..]));
     assert_eq!(rms, Ok((Partial::new(semicolon), &b" \t\r\n"[..])));
@@ -583,12 +719,12 @@
 
 #[test]
 #[cfg(feature = "std")]
-fn partial_recognize_take_while0() {
+fn partial_take_take_while0() {
     fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
         take_while(0.., AsChar::is_alphanum).parse_peek(i)
     }
     fn y(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        unpeek(x).recognize().parse_peek(i)
+        unpeek(x).take().parse_peek(i)
     }
     assert_eq!(
         x(Partial::new(&b"ab."[..])),
@@ -600,87 +736,128 @@
     );
 }
 
-#[cfg(feature = "alloc")]
 #[test]
-fn partial_case_insensitive() {
-    fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        tag(Caseless("ABcd")).parse_peek(i)
+fn partial_literal_case_insensitive() {
+    fn caseless_bytes(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+        literal(Caseless("ABcd")).parse_peek(i)
     }
     assert_eq!(
-        test(Partial::new(&b"aBCdefgh"[..])),
+        caseless_bytes(Partial::new(&b"aBCdefgh"[..])),
         Ok((Partial::new(&b"efgh"[..]), &b"aBCd"[..]))
     );
     assert_eq!(
-        test(Partial::new(&b"abcdefgh"[..])),
+        caseless_bytes(Partial::new(&b"abcdefgh"[..])),
         Ok((Partial::new(&b"efgh"[..]), &b"abcd"[..]))
     );
     assert_eq!(
-        test(Partial::new(&b"ABCDefgh"[..])),
+        caseless_bytes(Partial::new(&b"ABCDefgh"[..])),
         Ok((Partial::new(&b"efgh"[..]), &b"ABCD"[..]))
     );
     assert_eq!(
-        test(Partial::new(&b"ab"[..])),
+        caseless_bytes(Partial::new(&b"ab"[..])),
         Err(ErrMode::Incomplete(Needed::new(2)))
     );
     assert_eq!(
-        test(Partial::new(&b"Hello"[..])),
+        caseless_bytes(Partial::new(&b"Hello"[..])),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new(&b"Hello"[..]),
             ErrorKind::Tag
         )))
     );
     assert_eq!(
-        test(Partial::new(&b"Hel"[..])),
+        caseless_bytes(Partial::new(&b"Hel"[..])),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new(&b"Hel"[..]),
             ErrorKind::Tag
         )))
     );
 
-    fn test2(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
-        tag(Caseless("ABcd")).parse_peek(i)
+    fn caseless_str(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+        literal(Caseless("ABcd")).parse_peek(i)
     }
     assert_eq!(
-        test2(Partial::new("aBCdefgh")),
+        caseless_str(Partial::new("aBCdefgh")),
         Ok((Partial::new("efgh"), "aBCd"))
     );
     assert_eq!(
-        test2(Partial::new("abcdefgh")),
+        caseless_str(Partial::new("abcdefgh")),
         Ok((Partial::new("efgh"), "abcd"))
     );
     assert_eq!(
-        test2(Partial::new("ABCDefgh")),
+        caseless_str(Partial::new("ABCDefgh")),
         Ok((Partial::new("efgh"), "ABCD"))
     );
     assert_eq!(
-        test2(Partial::new("ab")),
+        caseless_str(Partial::new("ab")),
         Err(ErrMode::Incomplete(Needed::new(2)))
     );
     assert_eq!(
-        test2(Partial::new("Hello")),
+        caseless_str(Partial::new("Hello")),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new("Hello"),
             ErrorKind::Tag
         )))
     );
     assert_eq!(
-        test2(Partial::new("Hel")),
+        caseless_str(Partial::new("Hel")),
         Err(ErrMode::Backtrack(error_position!(
             &Partial::new("Hel"),
             ErrorKind::Tag
         )))
     );
+
+    fn matches_kelvin(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+        literal(Caseless("k")).parse_peek(i)
+    }
+    assert_eq!(
+        matches_kelvin(Partial::new("K")),
+        Err(ErrMode::Backtrack(error_position!(
+            &Partial::new("K"),
+            ErrorKind::Tag
+        )))
+    );
+
+    fn is_kelvin(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+        literal(Caseless("K")).parse_peek(i)
+    }
+    assert_eq!(
+        is_kelvin(Partial::new("k")),
+        Err(ErrMode::Backtrack(error_position!(
+            &Partial::new("k"),
+            ErrorKind::Tag
+        )))
+    );
 }
 
 #[test]
-fn partial_tag_fixed_size_array() {
+fn partial_literal_fixed_size_array() {
     fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        tag([0x42]).parse_peek(i)
+        literal([0x42]).parse_peek(i)
     }
     fn test2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
-        tag(&[0x42]).parse_peek(i)
+        literal(&[0x42]).parse_peek(i)
     }
     let input = Partial::new(&[0x42, 0x00][..]);
     assert_eq!(test(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..])));
     assert_eq!(test2(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..])));
 }
+
+#[test]
+fn rest_on_slices() {
+    let input: &[u8] = &b"Hello, world!"[..];
+    let empty: &[u8] = &b""[..];
+    assert_parse!(rest.parse_peek(input), Ok((empty, input)));
+}
+
+#[test]
+fn rest_on_strs() {
+    let input: &str = "Hello, world!";
+    let empty: &str = "";
+    assert_parse!(rest.parse_peek(input), Ok((empty, input)));
+}
+
+#[test]
+fn rest_len_on_slices() {
+    let input: &[u8] = &b"Hello, world!"[..];
+    assert_parse!(rest_len.parse_peek(input), Ok((input, input.len())));
+}
diff --git a/crates/winnow/src/trace.rs b/crates/winnow/src/trace.rs
deleted file mode 100644
index 9c05576..0000000
--- a/crates/winnow/src/trace.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//! Deprecated, replaced with [`winnow::combinator`][crate::combinator]
-
-/// Deprecated, replaced with [`winnow::combinator::trace`][crate::combinator::trace]
-#[deprecated(since = "0.5.35", note = "Replaced with `winnow::combinator::trace`")]
-#[inline(always)]
-pub fn trace<I: crate::stream::Stream, O, E>(
-    name: impl crate::lib::std::fmt::Display,
-    parser: impl crate::Parser<I, O, E>,
-) -> impl crate::Parser<I, O, E> {
-    crate::combinator::trace(name, parser)
-}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index e9cf7c7..c937ee0 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -216,7 +216,7 @@
  "glam",
  "glob",
  "googletest",
- "googletest_macro 0.13.0",
+ "googletest_macro",
  "gpio-cdev",
  "grpcio",
  "grpcio-compiler",
@@ -422,7 +422,7 @@
  "tokio-util",
  "toml 0.8.10",
  "toml_datetime",
- "toml_edit 0.22.4",
+ "toml_edit 0.22.20",
  "tonic",
  "tower",
  "tower-layer",
@@ -474,7 +474,7 @@
  "weak-table",
  "webpki",
  "which",
- "winnow",
+ "winnow 0.6.24",
  "x509-cert",
  "xml-rs",
  "yaml-rust",
@@ -2286,11 +2286,11 @@
 
 [[package]]
 name = "googletest"
-version = "0.11.0"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee44a233e2e661240ef77ed5de92ea9ecf70d4832963a8582a6681e1517f3673"
+checksum = "dce026f84cdd339bf71be01b24fe67470ee634282f68c1c4b563d00a9f002b05"
 dependencies = [
- "googletest_macro 0.11.0",
+ "googletest_macro",
  "num-traits",
  "regex",
  "rustversion",
@@ -2298,16 +2298,6 @@
 
 [[package]]
 name = "googletest_macro"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3057b7d1e628480193188ccb1a7850d034a3add3a350f4ed921b48e287ada9"
-dependencies = [
- "quote 1.0.38",
- "syn 2.0.96",
-]
-
-[[package]]
-name = "googletest_macro"
 version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f5070fa86976044fe2b004d874c10af5d1aed6d8f6a72ff93a6eb29cc87048bc"
@@ -5327,7 +5317,7 @@
  "serde",
  "serde_spanned",
  "toml_datetime",
- "toml_edit 0.22.4",
+ "toml_edit 0.22.20",
 ]
 
 [[package]]
@@ -5347,7 +5337,7 @@
 dependencies = [
  "indexmap 2.4.0",
  "toml_datetime",
- "winnow",
+ "winnow 0.5.37",
 ]
 
 [[package]]
@@ -5358,20 +5348,20 @@
 dependencies = [
  "indexmap 2.4.0",
  "toml_datetime",
- "winnow",
+ "winnow 0.5.37",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.4"
+version = "0.22.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"
+checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
 dependencies = [
  "indexmap 2.4.0",
  "serde",
  "serde_spanned",
  "toml_datetime",
- "winnow",
+ "winnow 0.6.24",
 ]
 
 [[package]]
@@ -6473,6 +6463,15 @@
 ]
 
 [[package]]
+name = "winnow"
+version = "0.6.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "wio"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 625543b..fe9a92a 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -130,7 +130,7 @@
 getrandom = "=0.2.15"
 glam = "=0.29.2"
 glob = "=0.3.2"
-googletest = "=0.11.0"
+googletest = "=0.13.0"
 googletest_macro = "=0.13.0"
 gpio-cdev = "=0.6.0"
 grpcio = "=0.13.0"
@@ -337,7 +337,7 @@
 tokio-util = "=0.7.13"
 toml = "=0.8.10"
 toml_datetime = "=0.6.8"
-toml_edit = "=0.22.4"
+toml_edit = "=0.22.20"
 tonic = "=0.11.0"
 tower = "=0.4.13"
 tower-layer = "=0.3.3"
@@ -389,7 +389,7 @@
 weak-table = "=0.3.2"
 webpki = "=0.22.4"
 which = "=4.4.0"
-winnow = "=0.5.37"
+winnow = "=0.6.24"
 x509-cert = "=0.2.5"
 xml-rs = "=0.8.19"
 yaml-rust = "=0.4.5"