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(>(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(>(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`:
+//!
//! 
//!
//! 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"