Merge "Update linkme and linkme-impl to 0.3.31" 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/prettyplease/.android-checksum.json b/crates/prettyplease/.android-checksum.json
index 00205b2..0df1f50 100644
--- a/crates/prettyplease/.android-checksum.json
+++ b/crates/prettyplease/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"7ae9e7a781bba8e321bc4192826c5653545bb4b05a74de388147bcbde6e2163c","Android.bp":"af0d7dff9013da50fad4d4ae2a6a72b3edcf9e1fb1e642ca3d25f7f27413e2cf","Cargo.toml":"6f04fc844b204a1332d7af8c9fe5c82dae95bfa01698382a272a609933f468bc","LICENSE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-APACHE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"ab3385c35023c2dee8d11dd63dd9a20099b563c2b891c541f33ffd17fb298444","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"6cd24eece6714dc53f7f9f979abdc9cae292e172340688029b4a6ef5ae91ae24","build.rs":"53df216257bb8fbdc2212e23f60cf6261007366a73e9dc390dde3dcbde3d6251","cargo_embargo.json":"0a3aea02264006cd539344c6021258d9fc7c2f88bbe7510b351f5e3485244e08","examples/input.rs":"c54e04743b9eb5665e7a2c0ea6cdd7d3a31fecec1258086b9268b8edf63c9bf7","examples/output.prettyplease.rs":"11211b3ec0b461c64494cec9f240eac5c60084d55a2f9048d41dcca66ebb6f02","examples/output.rustc.rs":"fe3e54f99bba0ad1aebace5e6a06cdf79548dac62524210064f1654a72968f96","examples/output.rustfmt.rs":"f5bcc4d1f873d76ce87834f9fa0d2e5c46042a6f0a2258b22ea4855a85e8b018","src/algorithm.rs":"0631caf6ac9fb58caf49dd2a321a096b21e6b7b4ee3b1e148a4b0e6529d59220","src/attr.rs":"fecce86077baf55f95f0c595579fb5ab1001aa27235a8ad48eaafe1a2aefd8a5","src/convenience.rs":"faae49b9678e3fa882f287594cac24d2544308dbd21a5762b396a174ceb975cd","src/data.rs":"dcb2d7eaa744b5d9190fc90d7cca25d64bff38dc57f746581d6097b79ca67d03","src/expr.rs":"ef67c23508873d9f33004fcb6b63d2163d8a0873a118d98576fea193dc6a5e05","src/file.rs":"359101ac28e7e39a275b9837053e6841765a48d022ea1482d8ef80e34d860d44","src/generics.rs":"73491a105c69318d01a6d090778d57102937f89fd9cd4cb8f2857c12a79e8dd5","src/item.rs":"c8f065dba1547b57c369e5b11956420d9e0979a6d5536225b370901a8160b7cc","src/iter.rs":"63d7cf3313d8ed3fa1637c5c01d80ab48b8a742538b71fd792604246822d4d9d","src/lib.rs":"bcce5af8455aa032e41a165cbe064b719b9289af13627057c89a6526066fd1cf","src/lifetime.rs":"b2f01d35e9faddd4a042dec30bb0f1668683ef11a9477e660e9eb4d480a08d61","src/lit.rs":"39cd69038e2143b16dad6007033015ef4c8e116b4af78e12937e916aa92b8376","src/mac.rs":"3df8a64cdb74ad433afe6a99f4d305bafbed0e7b2773f60fd7afaba91890a208","src/pat.rs":"d3ff013f425aa46d26d8c1c075c4bdd9e924f198125d5793d409941c31bf1ae9","src/path.rs":"c2c9a6f5fe868446427bb76ee17bbe14e6fb1fcd0d3d325fd3115f4057586638","src/ring.rs":"d2f765e033eea3aad1b8fd4351e8a3d32ff0c79be13a0104683d5fd38d0e131b","src/stmt.rs":"bce93e2b3af162871814b77ef1c350a4d0f71e423f74d1e5e93276dc39f1e23d","src/token.rs":"881884adcb68dcd01f6d66459c7f49df2f6f8019f0718d0a146d9c23ea375af5","src/ty.rs":"526800996453fd7068fc8f3e7e22a873f20bbcd89cd29f63e0cd32692b7559c1","tests/test.rs":"42b5f9d824aff128e6caf5df9602fe36b279b32d5debcaecf68969fbff09af4f"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"8754f5c95b4603c91b2fee04c7b22563bd3dac2e437fd8f33f861b95deda886d","Android.bp":"daa37880cfc6788215c9ac3b857bba421107f5dde60828f412396b57b64bddc5","Cargo.toml":"5ea38f196bfa1d0123a5f3129ff7cd7c2da6f9d1987c015d370236a0522722c3","LICENSE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-APACHE":"50f827348bfd5def2df4f30cb41264072cf9c180bd9ed698437e8d85b2482754","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"c6c17d8cdb6c025f663b86e078bd9350f16593db38063b9ff5708c981b48d2fd","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"6cd24eece6714dc53f7f9f979abdc9cae292e172340688029b4a6ef5ae91ae24","build.rs":"53df216257bb8fbdc2212e23f60cf6261007366a73e9dc390dde3dcbde3d6251","cargo_embargo.json":"0a3aea02264006cd539344c6021258d9fc7c2f88bbe7510b351f5e3485244e08","examples/input.rs":"c54e04743b9eb5665e7a2c0ea6cdd7d3a31fecec1258086b9268b8edf63c9bf7","examples/output.prettyplease.rs":"11211b3ec0b461c64494cec9f240eac5c60084d55a2f9048d41dcca66ebb6f02","examples/output.rustc.rs":"fe3e54f99bba0ad1aebace5e6a06cdf79548dac62524210064f1654a72968f96","examples/output.rustfmt.rs":"f5bcc4d1f873d76ce87834f9fa0d2e5c46042a6f0a2258b22ea4855a85e8b018","src/algorithm.rs":"b7ae58dedbca564ea449f02160084aa9a4f38a7b724afbdb19e81f7e524867ce","src/attr.rs":"dd3e5c4c2c4a65040f3b567c86405e823e8eb1f963aaa8b8a8547fc1de22ca31","src/classify.rs":"e17fd6644f59523bd6f7a198acf3645fd1a0930f5cbfebe5552918ea8841e38e","src/convenience.rs":"faae49b9678e3fa882f287594cac24d2544308dbd21a5762b396a174ceb975cd","src/data.rs":"25457e0091113479a29087a215dc22f32eb28c9f4e29fb43f8864f81d6b98bf3","src/expr.rs":"fe7d9645bc3f86c63a58bc72aeb69b4ad11bc0651cc37b4e4f6e3b5a20708275","src/file.rs":"359101ac28e7e39a275b9837053e6841765a48d022ea1482d8ef80e34d860d44","src/fixup.rs":"2f258a9d48fc6851ec32dc7bfc492f1187c2438fe3504b3a73ea0dfa91087d32","src/generics.rs":"eab714ff712509d0e2b23a635eb6ebabeb711d82b8e77350ace505180e5f60ef","src/item.rs":"21d89d8eb6376057bc3ffcfe7e0f18cb72803e1656fac5ec6919a93a85ef2939","src/iter.rs":"63d7cf3313d8ed3fa1637c5c01d80ab48b8a742538b71fd792604246822d4d9d","src/lib.rs":"c4700c7472d0a9eeb43c25f9f42187e75d203dd0d07416649c5d588ef64f721c","src/lifetime.rs":"b2f01d35e9faddd4a042dec30bb0f1668683ef11a9477e660e9eb4d480a08d61","src/lit.rs":"39cd69038e2143b16dad6007033015ef4c8e116b4af78e12937e916aa92b8376","src/mac.rs":"131791d63273f8ae09cbb301635f9e2cbee3f8b5dd89a55c376590b15b7de911","src/pat.rs":"40e2926be48bcdfd151e6de00880ad4aba6c7822db3eb340e3f12a6a50edae4b","src/path.rs":"68c7066f63d9eb570d0d5a613225f2948d0d547257ecd49ea29e7cf693587d24","src/precedence.rs":"939141754675dbb9bc6091123a847d62cf8493db0aa701686f833b0435d3fc40","src/ring.rs":"cd1a93144b2f76a6a47ad8847ddc90d9b3d13bf674243e774f7848d7baebbcd8","src/stmt.rs":"10372120c97c4f3840f7b494bf973c36c97625b68f60f178854726b622b5fc0d","src/token.rs":"881884adcb68dcd01f6d66459c7f49df2f6f8019f0718d0a146d9c23ea375af5","src/ty.rs":"e8d75df0fe6e83a50a288b6b0ffe2285d698810043e6ba6a59408b62ecb04383","tests/test.rs":"adf75b6cb0fe9c700ea366366e2b0b9a2e166983b4dfd5abe87c1bcc986cbdea","tests/test_precedence.rs":"64413a1314a13c027b8e5e6afbfcff4d3375f5db80131fffc7b341d89fb775a6"}}
\ No newline at end of file
diff --git a/crates/prettyplease/.cargo-checksum.json b/crates/prettyplease/.cargo-checksum.json
index 434d068..823e7b4 100644
--- a/crates/prettyplease/.cargo-checksum.json
+++ b/crates/prettyplease/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"0e165ac6d5ca01781b9c1586f976e6abeedaa534f1ba0dc0adf607f9b30795e0","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a7e6d152cdc6ea603077e50b8d55af374d9d21fd9f62d08a008588b17d785e6e","build.rs":"79a5b2d260aa97aeac7105fbfa00774982f825cd708c100ea96d01c39974bb88","examples/input.rs":"53350088f12a346a99034af41ef432dedcc9e5d581c5592d9aae3807c42656c1","examples/output.prettyplease.rs":"fa63c118daadb64c456ec5b8d5e46e5d7fabbbeb6a6e61a08eabc23360a18fbd","examples/output.rustc.rs":"0c66f8929fa40a2822d4ea1aec3d8b83db598aba043b17f3c1a6133f0d657135","examples/output.rustfmt.rs":"914a9aea1c51e097bfd80c9af4011811e6126c9df5fb0eac3d40b1203fba7c58","src/algorithm.rs":"7a4a7b62f5300aabf7fcff3cd8c8aaa3ae1e3d1dc717eea1e4797988ce0e18c7","src/attr.rs":"54e829ae468f22c8e2853d669515575f1444bfee026cfd9b19538f77caf10ab7","src/convenience.rs":"dd392b009b691d3587c7d8e3caeaacf450303c4223792b5f89c336358e371c39","src/data.rs":"9db6623d3ccc79b541a28bdc88875ad0036576689e085007eb362819f8e9a2d3","src/expr.rs":"45a30de0afdf8e0564c221ebcb4cf11e2d8948b5b85cc99d5d55d1293e65dea9","src/file.rs":"5689efa3c5959a6a0d8cfc2c13bf8a37ab0669e2b81dbded3f3c28884a88fca0","src/generics.rs":"14a01f0602c005939f0324ef5e6b6624f477a531552bc764447e08875fb2ebbb","src/item.rs":"6793b19d8be3dd7cb75a984370a771d0592b9c4548d294cf246424947a3c8535","src/iter.rs":"38b2cd3b38719c6024fb6b3aa739f6f8736c83193fd21e2365d4f6c27bc41666","src/lib.rs":"a6c8e3e455f6b2f5b0800213464076f8ac49fc64aeddd77f3e4f0f9dbf554b18","src/lifetime.rs":"6d420430168185b2da3409bc38a45f63cced9443915f04e6aec71367fc070dcf","src/lit.rs":"9ea6d25533e64df4ff01c084fa1c31ddf64fb3b159409eec7d80dbf281e5171e","src/mac.rs":"62911747c308187afc750b4bd4f8bd24ee825081043d78da68a001ea55ab5853","src/pat.rs":"d130b141a3fd8098913cb179efe6600a5f010a2719447ecb3d4fb29e9b546220","src/path.rs":"4d3f7caa0bfe821bdb9ad00ee1d7d61101aef8a5cbea35d565b6da6217feefca","src/ring.rs":"e23d133209b977e457b07b0cd93b3711d01f4172d7cfa4cf6a7247637390e606","src/stmt.rs":"763f617a5535f8e61593b0cb1c6c9f5caef032085671dbce509b691d94d39835","src/token.rs":"c288b1d81f2a35673d4ca1dd10d3386670b067460121df3038303e1ed73b41a7","src/ty.rs":"b5fa5f318c5e4593ed3eed4707e63dceaa62eea4ecdfba0f59bd946917ca3dbd","tests/test.rs":"04994ad0a37c43457390f11ade6ab74fc26c5e879b0a374f0b64acb530a0a496"},"package":"64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"}
\ No newline at end of file
+{"files":{"Cargo.toml":"06e68c5a91a65141eef6ddfbc87652ffa90a3ab71175b31021d61e6eb8e7b8e1","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a7e6d152cdc6ea603077e50b8d55af374d9d21fd9f62d08a008588b17d785e6e","build.rs":"79a5b2d260aa97aeac7105fbfa00774982f825cd708c100ea96d01c39974bb88","examples/input.rs":"53350088f12a346a99034af41ef432dedcc9e5d581c5592d9aae3807c42656c1","examples/output.prettyplease.rs":"fa63c118daadb64c456ec5b8d5e46e5d7fabbbeb6a6e61a08eabc23360a18fbd","examples/output.rustc.rs":"0c66f8929fa40a2822d4ea1aec3d8b83db598aba043b17f3c1a6133f0d657135","examples/output.rustfmt.rs":"914a9aea1c51e097bfd80c9af4011811e6126c9df5fb0eac3d40b1203fba7c58","src/algorithm.rs":"bae517cd89743dd4bccb7cf7bf659c9e138d68cf4b06030446effb794811a36c","src/attr.rs":"c11f2ed0d16f2a7c1a0023f2fc5c81a074276ff399679b2814ab762edb8dd792","src/classify.rs":"2ce2d63ad9071aac10b1037e6382703736e0147d96b3ccf32a53182d12883f1b","src/convenience.rs":"dd392b009b691d3587c7d8e3caeaacf450303c4223792b5f89c336358e371c39","src/data.rs":"5bc2dce1cfa1aa5c1324ccdc2d76a6bd5df2382530c7e863d2bb50dea60cc4bc","src/expr.rs":"e99c5ef631cff8a0b0c2666deb35cd0c3eee2ee3585b113ff37cffe3b12435d4","src/file.rs":"5689efa3c5959a6a0d8cfc2c13bf8a37ab0669e2b81dbded3f3c28884a88fca0","src/fixup.rs":"ee279996cd5c57eb308a7e8529cd1f2999617426047df2e641a95ddc82ff44ce","src/generics.rs":"f10b95f4b011f5bf6510d3a77e38227716dccf0a8aeb8a8344e776be9f90f54e","src/item.rs":"4dc320bf73f4ca7d2c6741c31700b5150a02106bfe688969070aff53c2ce455a","src/iter.rs":"38b2cd3b38719c6024fb6b3aa739f6f8736c83193fd21e2365d4f6c27bc41666","src/lib.rs":"f92f24de5bc5231d107bf97c10d845f83e6c0ae922c8d33841f8c34d07c76130","src/lifetime.rs":"6d420430168185b2da3409bc38a45f63cced9443915f04e6aec71367fc070dcf","src/lit.rs":"9ea6d25533e64df4ff01c084fa1c31ddf64fb3b159409eec7d80dbf281e5171e","src/mac.rs":"36c62d1b721c6c56af799c55371ee43e9c446c4787ba1f69021b9cb6acda76b9","src/pat.rs":"8e53fd1b5382bb068210162bfab9921246093cfdd80dd93cd8627fcfdae39940","src/path.rs":"e73d83dc38f5c6c0c82f824da7eb090a16027f32fc40446b185580ee5e99be58","src/precedence.rs":"a8ce97ba0a25f442b5f238c64f078d70f4114b4b0f9df82764d533dd39a47abb","src/ring.rs":"517b1a02f8e0a9c1316830117daad1e30d17e1fcf6428c6b438c626aa43286ae","src/stmt.rs":"e17ab9647fed9daa4f5b2fbd007015128f2a7fc65686a988593444a37242f885","src/token.rs":"c288b1d81f2a35673d4ca1dd10d3386670b067460121df3038303e1ed73b41a7","src/ty.rs":"a1e3e5a08124673826948f97e70c11800081d2bca7f3aec12d84d0d00837290f","tests/test.rs":"c6f8c7830b7491fca1d56e41aa4acc6256b683a3556a48982f57ae62d38aaaa2","tests/test_precedence.rs":"de0c770b9a72e5eba8a52dcac0614d6db8ff5041ba601e1e67f113d68c9afd50"},"package":"6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"}
\ No newline at end of file
diff --git a/crates/prettyplease/Android.bp b/crates/prettyplease/Android.bp
index 8e5fd1a..1e0bada 100644
--- a/crates/prettyplease/Android.bp
+++ b/crates/prettyplease/Android.bp
@@ -18,7 +18,7 @@
host_cross_supported: false,
crate_name: "prettyplease",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.25",
+ cargo_pkg_version: "0.2.29",
crate_root: "src/lib.rs",
edition: "2021",
rustlibs: [
diff --git a/crates/prettyplease/Cargo.toml b/crates/prettyplease/Cargo.toml
index b39c5e3..14dd304 100644
--- a/crates/prettyplease/Cargo.toml
+++ b/crates/prettyplease/Cargo.toml
@@ -11,9 +11,9 @@
[package]
edition = "2021"
-rust-version = "1.61"
+rust-version = "1.62"
name = "prettyplease"
-version = "0.2.25"
+version = "0.2.29"
authors = ["David Tolnay <[email protected]>"]
build = "build.rs"
links = "prettyplease02"
@@ -38,6 +38,9 @@
[package.metadata.playground]
features = ["verbatim"]
+[features]
+verbatim = ["syn/parsing"]
+
[lib]
name = "prettyplease"
path = "src/lib.rs"
@@ -47,12 +50,16 @@
name = "test"
path = "tests/test.rs"
+[[test]]
+name = "test_precedence"
+path = "tests/test_precedence.rs"
+
[dependencies.proc-macro2]
version = "1.0.80"
default-features = false
[dependencies.syn]
-version = "2.0.81"
+version = "2.0.96"
features = ["full"]
default-features = false
@@ -68,9 +75,12 @@
default-features = false
[dev-dependencies.syn]
-version = "2.0.81"
-features = ["parsing"]
+version = "2.0.96"
+features = [
+ "clone-impls",
+ "extra-traits",
+ "parsing",
+ "printing",
+ "visit-mut",
+]
default-features = false
-
-[features]
-verbatim = ["syn/parsing"]
diff --git a/crates/prettyplease/METADATA b/crates/prettyplease/METADATA
index 13f0ff2..8344b06 100644
--- a/crates/prettyplease/METADATA
+++ b/crates/prettyplease/METADATA
@@ -1,17 +1,17 @@
name: "prettyplease"
description: "A minimal `syn` syntax tree pretty-printer"
third_party {
- version: "0.2.25"
+ version: "0.2.29"
license_type: NOTICE
last_upgrade_date {
- year: 2024
- month: 12
- day: 20
+ year: 2025
+ month: 1
+ day: 14
}
homepage: "https://crates.io/crates/prettyplease"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/prettyplease/prettyplease-0.2.25.crate"
- version: "0.2.25"
+ value: "https://static.crates.io/crates/prettyplease/prettyplease-0.2.29.crate"
+ version: "0.2.29"
}
}
diff --git a/crates/prettyplease/src/algorithm.rs b/crates/prettyplease/src/algorithm.rs
index 6e2b961..ecb68c3 100644
--- a/crates/prettyplease/src/algorithm.rs
+++ b/crates/prettyplease/src/algorithm.rs
@@ -19,7 +19,7 @@
pub offset: isize,
pub blank_space: usize,
pub pre_break: Option<char>,
- pub post_break: Option<char>,
+ pub post_break: &'static str,
pub no_break: Option<char>,
pub if_nonempty: bool,
pub never_break: bool,
@@ -211,9 +211,18 @@
self.scan_end();
}
+ pub fn ends_with(&self, ch: char) -> bool {
+ for i in self.buf.index_range().rev() {
+ if let Token::String(token) = &self.buf[i].token {
+ return token.ends_with(ch);
+ }
+ }
+ self.out.ends_with(ch)
+ }
+
fn check_stream(&mut self) {
while self.right_total - self.left_total > self.space {
- if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
+ if *self.scan_stack.front().unwrap() == self.buf.index_range().start {
self.scan_stack.pop_front().unwrap();
self.buf.first_mut().size = SIZE_INFINITY;
}
@@ -353,10 +362,10 @@
let indent = self.indent as isize + token.offset;
self.pending_indentation = usize::try_from(indent).unwrap();
self.space = cmp::max(MARGIN - indent, MIN_SPACE);
- if let Some(post_break) = token.post_break {
+ if !token.post_break.is_empty() {
self.print_indent();
- self.out.push(post_break);
- self.space -= post_break.len_utf8() as isize;
+ self.out.push_str(token.post_break);
+ self.space -= token.post_break.len() as isize;
}
}
}
diff --git a/crates/prettyplease/src/attr.rs b/crates/prettyplease/src/attr.rs
index 0388d66..d7c2250 100644
--- a/crates/prettyplease/src/attr.rs
+++ b/crates/prettyplease/src/attr.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::path::PathKind;
use crate::INDENT;
use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
@@ -102,7 +103,7 @@
fn meta_name_value(&mut self, meta: &MetaNameValue) {
self.path(&meta.path, PathKind::Simple);
self.word(" = ");
- self.expr(&meta.value);
+ self.expr(&meta.value, FixupContext::NONE);
}
fn attr_tokens(&mut self, tokens: TokenStream) {
diff --git a/crates/prettyplease/src/classify.rs b/crates/prettyplease/src/classify.rs
new file mode 100644
index 0000000..17648f6
--- /dev/null
+++ b/crates/prettyplease/src/classify.rs
@@ -0,0 +1,324 @@
+use proc_macro2::{Delimiter, TokenStream, TokenTree};
+use std::ops::ControlFlow;
+use syn::punctuated::Punctuated;
+use syn::{Expr, MacroDelimiter, Path, PathArguments, ReturnType, Token, Type, TypeParamBound};
+
+pub(crate) fn requires_semi_to_be_stmt(expr: &Expr) -> bool {
+ match expr {
+ Expr::Macro(expr) => !matches!(expr.mac.delimiter, MacroDelimiter::Brace(_)),
+ _ => requires_comma_to_be_match_arm(expr),
+ }
+}
+
+pub(crate) fn requires_comma_to_be_match_arm(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::If(_)
+ | Expr::Match(_)
+ | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc
+ | Expr::While(_)
+ | Expr::Loop(_)
+ | Expr::ForLoop(_)
+ | Expr::TryBlock(_)
+ | Expr::Const(_) => return false,
+
+ Expr::Array(_)
+ | Expr::Assign(_)
+ | Expr::Async(_)
+ | Expr::Await(_)
+ | Expr::Binary(_)
+ | Expr::Break(_)
+ | Expr::Call(_)
+ | Expr::Cast(_)
+ | Expr::Closure(_)
+ | Expr::Continue(_)
+ | Expr::Field(_)
+ | Expr::Index(_)
+ | Expr::Infer(_)
+ | Expr::Let(_)
+ | Expr::Lit(_)
+ | Expr::Macro(_)
+ | Expr::MethodCall(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::Range(_)
+ | Expr::RawAddr(_)
+ | Expr::Reference(_)
+ | Expr::Repeat(_)
+ | Expr::Return(_)
+ | Expr::Struct(_)
+ | Expr::Try(_)
+ | Expr::Tuple(_)
+ | Expr::Unary(_)
+ | Expr::Yield(_)
+ | Expr::Verbatim(_) => return true,
+
+ Expr::Group(group) => expr = &group.expr,
+
+ _ => return true,
+ }
+ }
+}
+
+pub(crate) fn trailing_unparameterized_path(mut ty: &Type) -> bool {
+ loop {
+ match ty {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Type::BareFn(t) => match &t.output {
+ ReturnType::Default => return false,
+ ReturnType::Type(_, ret) => ty = ret,
+ },
+ Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_path) => return trailing_path,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Path(t) => match last_type_in_path(&t.path) {
+ ControlFlow::Break(trailing_path) => return trailing_path,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Ptr(t) => ty = &t.elem,
+ Type::Reference(t) => ty = &t.elem,
+ Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_path) => return trailing_path,
+ ControlFlow::Continue(t) => ty = t,
+ },
+
+ Type::Array(_)
+ | Type::Group(_)
+ | Type::Infer(_)
+ | Type::Macro(_)
+ | Type::Never(_)
+ | Type::Paren(_)
+ | Type::Slice(_)
+ | Type::Tuple(_)
+ | Type::Verbatim(_) => return false,
+
+ _ => return false,
+ }
+ }
+
+ fn last_type_in_path(path: &Path) -> ControlFlow<bool, &Type> {
+ match &path.segments.last().unwrap().arguments {
+ PathArguments::None => ControlFlow::Break(true),
+ PathArguments::AngleBracketed(_) => ControlFlow::Break(false),
+ PathArguments::Parenthesized(arg) => match &arg.output {
+ ReturnType::Default => ControlFlow::Break(false),
+ ReturnType::Type(_, ret) => ControlFlow::Continue(ret),
+ },
+ }
+ }
+
+ fn last_type_in_bounds(
+ bounds: &Punctuated<TypeParamBound, Token![+]>,
+ ) -> ControlFlow<bool, &Type> {
+ match bounds.last().unwrap() {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ TypeParamBound::Trait(t) => last_type_in_path(&t.path),
+ TypeParamBound::Lifetime(_)
+ | TypeParamBound::PreciseCapture(_)
+ | TypeParamBound::Verbatim(_) => ControlFlow::Break(false),
+ _ => ControlFlow::Break(false),
+ }
+ }
+}
+
+/// Whether the expression's first token is the label of a loop/block.
+pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Block(e) => return e.label.is_some(),
+ Expr::ForLoop(e) => return e.label.is_some(),
+ Expr::Loop(e) => return e.label.is_some(),
+ Expr::While(e) => return e.label.is_some(),
+
+ Expr::Assign(e) => expr = &e.left,
+ Expr::Await(e) => expr = &e.base,
+ Expr::Binary(e) => expr = &e.left,
+ Expr::Call(e) => expr = &e.func,
+ Expr::Cast(e) => expr = &e.expr,
+ Expr::Field(e) => expr = &e.base,
+ Expr::Index(e) => expr = &e.expr,
+ Expr::MethodCall(e) => expr = &e.receiver,
+ Expr::Range(e) => match &e.start {
+ Some(start) => expr = start,
+ None => return false,
+ },
+ Expr::Try(e) => expr = &e.expr,
+
+ Expr::Array(_)
+ | Expr::Async(_)
+ | Expr::Break(_)
+ | Expr::Closure(_)
+ | Expr::Const(_)
+ | Expr::Continue(_)
+ | Expr::If(_)
+ | Expr::Infer(_)
+ | Expr::Let(_)
+ | Expr::Lit(_)
+ | Expr::Macro(_)
+ | Expr::Match(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::RawAddr(_)
+ | Expr::Reference(_)
+ | Expr::Repeat(_)
+ | Expr::Return(_)
+ | Expr::Struct(_)
+ | Expr::TryBlock(_)
+ | Expr::Tuple(_)
+ | Expr::Unary(_)
+ | Expr::Unsafe(_)
+ | Expr::Verbatim(_)
+ | Expr::Yield(_) => return false,
+
+ Expr::Group(e) => {
+ if !e.attrs.is_empty() {
+ return false;
+ }
+ expr = &e.expr;
+ }
+
+ _ => return false,
+ }
+ }
+}
+
+/// Whether the expression's last token is `}`.
+pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Async(_)
+ | Expr::Block(_)
+ | Expr::Const(_)
+ | Expr::ForLoop(_)
+ | Expr::If(_)
+ | Expr::Loop(_)
+ | Expr::Match(_)
+ | Expr::Struct(_)
+ | Expr::TryBlock(_)
+ | Expr::Unsafe(_)
+ | Expr::While(_) => return true,
+
+ Expr::Assign(e) => expr = &e.right,
+ Expr::Binary(e) => expr = &e.right,
+ Expr::Break(e) => match &e.expr {
+ Some(e) => expr = e,
+ None => return false,
+ },
+ Expr::Cast(e) => return type_trailing_brace(&e.ty),
+ Expr::Closure(e) => expr = &e.body,
+ Expr::Group(e) => expr = &e.expr,
+ Expr::Let(e) => expr = &e.expr,
+ Expr::Macro(e) => return matches!(e.mac.delimiter, MacroDelimiter::Brace(_)),
+ Expr::Range(e) => match &e.end {
+ Some(end) => expr = end,
+ None => return false,
+ },
+ Expr::RawAddr(e) => expr = &e.expr,
+ Expr::Reference(e) => expr = &e.expr,
+ Expr::Return(e) => match &e.expr {
+ Some(e) => expr = e,
+ None => return false,
+ },
+ Expr::Unary(e) => expr = &e.expr,
+ Expr::Verbatim(e) => return tokens_trailing_brace(e),
+ Expr::Yield(e) => match &e.expr {
+ Some(e) => expr = e,
+ None => return false,
+ },
+
+ Expr::Array(_)
+ | Expr::Await(_)
+ | Expr::Call(_)
+ | Expr::Continue(_)
+ | Expr::Field(_)
+ | Expr::Index(_)
+ | Expr::Infer(_)
+ | Expr::Lit(_)
+ | Expr::MethodCall(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::Repeat(_)
+ | Expr::Try(_)
+ | Expr::Tuple(_) => return false,
+
+ _ => return false,
+ }
+ }
+
+ fn type_trailing_brace(mut ty: &Type) -> bool {
+ loop {
+ match ty {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Type::BareFn(t) => match &t.output {
+ ReturnType::Default => return false,
+ ReturnType::Type(_, ret) => ty = ret,
+ },
+ Type::ImplTrait(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_brace) => return trailing_brace,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Macro(t) => return matches!(t.mac.delimiter, MacroDelimiter::Brace(_)),
+ Type::Path(t) => match last_type_in_path(&t.path) {
+ Some(t) => ty = t,
+ None => return false,
+ },
+ Type::Ptr(t) => ty = &t.elem,
+ Type::Reference(t) => ty = &t.elem,
+ Type::TraitObject(t) => match last_type_in_bounds(&t.bounds) {
+ ControlFlow::Break(trailing_brace) => return trailing_brace,
+ ControlFlow::Continue(t) => ty = t,
+ },
+ Type::Verbatim(t) => return tokens_trailing_brace(t),
+
+ Type::Array(_)
+ | Type::Group(_)
+ | Type::Infer(_)
+ | Type::Never(_)
+ | Type::Paren(_)
+ | Type::Slice(_)
+ | Type::Tuple(_) => return false,
+
+ _ => return false,
+ }
+ }
+ }
+
+ fn last_type_in_path(path: &Path) -> Option<&Type> {
+ match &path.segments.last().unwrap().arguments {
+ PathArguments::None | PathArguments::AngleBracketed(_) => None,
+ PathArguments::Parenthesized(arg) => match &arg.output {
+ ReturnType::Default => None,
+ ReturnType::Type(_, ret) => Some(ret),
+ },
+ }
+ }
+
+ fn last_type_in_bounds(
+ bounds: &Punctuated<TypeParamBound, Token![+]>,
+ ) -> ControlFlow<bool, &Type> {
+ match bounds.last().unwrap() {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ TypeParamBound::Trait(t) => match last_type_in_path(&t.path) {
+ Some(t) => ControlFlow::Continue(t),
+ None => ControlFlow::Break(false),
+ },
+ TypeParamBound::Lifetime(_) | TypeParamBound::PreciseCapture(_) => {
+ ControlFlow::Break(false)
+ }
+ TypeParamBound::Verbatim(t) => ControlFlow::Break(tokens_trailing_brace(t)),
+ _ => ControlFlow::Break(false),
+ }
+ }
+
+ fn tokens_trailing_brace(tokens: &TokenStream) -> bool {
+ if let Some(TokenTree::Group(last)) = tokens.clone().into_iter().last() {
+ last.delimiter() == Delimiter::Brace
+ } else {
+ false
+ }
+ }
+}
diff --git a/crates/prettyplease/src/data.rs b/crates/prettyplease/src/data.rs
index d823ee3..3561a49 100644
--- a/crates/prettyplease/src/data.rs
+++ b/crates/prettyplease/src/data.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -31,7 +32,7 @@
}
if let Some((_eq_token, discriminant)) = &variant.discriminant {
self.word(" = ");
- self.expr(discriminant);
+ self.expr(discriminant, FixupContext::NONE);
}
}
diff --git a/crates/prettyplease/src/expr.rs b/crates/prettyplease/src/expr.rs
index 52c9e5a..7b223b8 100644
--- a/crates/prettyplease/src/expr.rs
+++ b/crates/prettyplease/src/expr.rs
@@ -1,7 +1,10 @@
use crate::algorithm::{BreakToken, Printer};
use crate::attr;
+use crate::classify;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
+use crate::precedence::Precedence;
use crate::stmt;
use crate::INDENT;
use proc_macro2::TokenStream;
@@ -17,94 +20,130 @@
};
impl Printer {
- pub fn expr(&mut self, expr: &Expr) {
+ pub fn expr(&mut self, expr: &Expr, mut fixup: FixupContext) {
+ let needs_paren = fixup.parenthesize(expr);
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
let beginning_of_line = false;
+
match expr {
#![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
Expr::Array(expr) => self.expr_array(expr),
- Expr::Assign(expr) => self.expr_assign(expr),
+ Expr::Assign(expr) => self.expr_assign(expr, fixup),
Expr::Async(expr) => self.expr_async(expr),
- Expr::Await(expr) => self.expr_await(expr, beginning_of_line),
- Expr::Binary(expr) => self.expr_binary(expr),
+ Expr::Await(expr) => self.expr_await(expr, beginning_of_line, fixup),
+ Expr::Binary(expr) => self.expr_binary(expr, fixup),
Expr::Block(expr) => self.expr_block(expr),
- Expr::Break(expr) => self.expr_break(expr),
- Expr::Call(expr) => self.expr_call(expr, beginning_of_line),
- Expr::Cast(expr) => self.expr_cast(expr),
- Expr::Closure(expr) => self.expr_closure(expr),
+ Expr::Break(expr) => self.expr_break(expr, fixup),
+ Expr::Call(expr) => self.expr_call(expr, beginning_of_line, fixup),
+ Expr::Cast(expr) => self.expr_cast(expr, fixup),
+ Expr::Closure(expr) => self.expr_closure(expr, fixup),
Expr::Const(expr) => self.expr_const(expr),
Expr::Continue(expr) => self.expr_continue(expr),
- Expr::Field(expr) => self.expr_field(expr, beginning_of_line),
+ Expr::Field(expr) => self.expr_field(expr, beginning_of_line, fixup),
Expr::ForLoop(expr) => self.expr_for_loop(expr),
- Expr::Group(expr) => self.expr_group(expr),
+ Expr::Group(expr) => self.expr_group(expr, fixup),
Expr::If(expr) => self.expr_if(expr),
- Expr::Index(expr) => self.expr_index(expr, beginning_of_line),
+ Expr::Index(expr) => self.expr_index(expr, beginning_of_line, fixup),
Expr::Infer(expr) => self.expr_infer(expr),
- Expr::Let(expr) => self.expr_let(expr),
+ Expr::Let(expr) => self.expr_let(expr, fixup),
Expr::Lit(expr) => self.expr_lit(expr),
Expr::Loop(expr) => self.expr_loop(expr),
Expr::Macro(expr) => self.expr_macro(expr),
Expr::Match(expr) => self.expr_match(expr),
- Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line),
+ Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line, fixup),
Expr::Paren(expr) => self.expr_paren(expr),
Expr::Path(expr) => self.expr_path(expr),
- Expr::Range(expr) => self.expr_range(expr),
- Expr::RawAddr(expr) => self.expr_raw_addr(expr),
- Expr::Reference(expr) => self.expr_reference(expr),
+ Expr::Range(expr) => self.expr_range(expr, fixup),
+ Expr::RawAddr(expr) => self.expr_raw_addr(expr, fixup),
+ Expr::Reference(expr) => self.expr_reference(expr, fixup),
Expr::Repeat(expr) => self.expr_repeat(expr),
- Expr::Return(expr) => self.expr_return(expr),
+ Expr::Return(expr) => self.expr_return(expr, fixup),
Expr::Struct(expr) => self.expr_struct(expr),
- Expr::Try(expr) => self.expr_try(expr, beginning_of_line),
+ Expr::Try(expr) => self.expr_try(expr, beginning_of_line, fixup),
Expr::TryBlock(expr) => self.expr_try_block(expr),
Expr::Tuple(expr) => self.expr_tuple(expr),
- Expr::Unary(expr) => self.expr_unary(expr),
+ Expr::Unary(expr) => self.expr_unary(expr, fixup),
Expr::Unsafe(expr) => self.expr_unsafe(expr),
- Expr::Verbatim(expr) => self.expr_verbatim(expr),
+ Expr::Verbatim(expr) => self.expr_verbatim(expr, fixup),
Expr::While(expr) => self.expr_while(expr),
- Expr::Yield(expr) => self.expr_yield(expr),
+ Expr::Yield(expr) => self.expr_yield(expr, fixup),
_ => unimplemented!("unknown Expr"),
}
- }
- pub fn expr_beginning_of_line(&mut self, expr: &Expr, beginning_of_line: bool) {
- match expr {
- Expr::Await(expr) => self.expr_await(expr, beginning_of_line),
- Expr::Field(expr) => self.expr_field(expr, beginning_of_line),
- Expr::Index(expr) => self.expr_index(expr, beginning_of_line),
- Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line),
- Expr::Try(expr) => self.expr_try(expr, beginning_of_line),
- _ => self.expr(expr),
- }
- }
-
- fn subexpr(&mut self, expr: &Expr, beginning_of_line: bool) {
- match expr {
- Expr::Await(expr) => self.subexpr_await(expr, beginning_of_line),
- Expr::Call(expr) => self.subexpr_call(expr),
- Expr::Field(expr) => self.subexpr_field(expr, beginning_of_line),
- Expr::Index(expr) => self.subexpr_index(expr, beginning_of_line),
- Expr::MethodCall(expr) => {
- let unindent_call_args = false;
- self.subexpr_method_call(expr, beginning_of_line, unindent_call_args);
- }
- Expr::Try(expr) => self.subexpr_try(expr, beginning_of_line),
- _ => {
- self.cbox(-INDENT);
- self.expr(expr);
- self.end();
- }
- }
- }
-
- fn wrap_exterior_struct(&mut self, expr: &Expr) {
- let needs_paren = contains_exterior_struct_lit(expr);
- if needs_paren {
- self.word("(");
- }
- self.cbox(0);
- self.expr(expr);
if needs_paren {
self.word(")");
}
+ }
+
+ pub fn expr_beginning_of_line(
+ &mut self,
+ expr: &Expr,
+ mut needs_paren: bool,
+ beginning_of_line: bool,
+ mut fixup: FixupContext,
+ ) {
+ needs_paren |= fixup.parenthesize(expr);
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
+ match expr {
+ Expr::Await(expr) => self.expr_await(expr, beginning_of_line, fixup),
+ Expr::Field(expr) => self.expr_field(expr, beginning_of_line, fixup),
+ Expr::Index(expr) => self.expr_index(expr, beginning_of_line, fixup),
+ Expr::MethodCall(expr) => self.expr_method_call(expr, beginning_of_line, fixup),
+ Expr::Try(expr) => self.expr_try(expr, beginning_of_line, fixup),
+ _ => self.expr(expr, fixup),
+ }
+
+ if needs_paren {
+ self.word(")");
+ }
+ }
+
+ fn prefix_subexpr(
+ &mut self,
+ expr: &Expr,
+ mut needs_paren: bool,
+ beginning_of_line: bool,
+ mut fixup: FixupContext,
+ ) {
+ needs_paren |= fixup.parenthesize(expr);
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
+ match expr {
+ Expr::Await(expr) => self.prefix_subexpr_await(expr, beginning_of_line, fixup),
+ Expr::Call(expr) => self.prefix_subexpr_call(expr, fixup),
+ Expr::Field(expr) => self.prefix_subexpr_field(expr, beginning_of_line, fixup),
+ Expr::Index(expr) => self.prefix_subexpr_index(expr, beginning_of_line, fixup),
+ Expr::MethodCall(expr) => {
+ let unindent_call_args = false;
+ self.prefix_subexpr_method_call(expr, beginning_of_line, unindent_call_args, fixup);
+ }
+ Expr::Try(expr) => self.prefix_subexpr_try(expr, beginning_of_line, fixup),
+ _ => {
+ self.cbox(-INDENT);
+ self.expr(expr, fixup);
+ self.end();
+ }
+ }
+
+ if needs_paren {
+ self.word(")");
+ }
+ }
+
+ fn expr_condition(&mut self, expr: &Expr) {
+ self.cbox(0);
+ self.expr(expr, FixupContext::new_condition());
if needs_newline_if_wrap(expr) {
self.space();
} else {
@@ -113,13 +152,26 @@
self.end();
}
+ pub fn subexpr(&mut self, expr: &Expr, needs_paren: bool, mut fixup: FixupContext) {
+ if needs_paren {
+ self.word("(");
+ fixup = FixupContext::NONE;
+ }
+
+ self.expr(expr, fixup);
+
+ if needs_paren {
+ self.word(")");
+ }
+ }
+
fn expr_array(&mut self, expr: &ExprArray) {
self.outer_attrs(&expr.attrs);
self.word("[");
self.cbox(INDENT);
self.zerobreak();
for element in expr.elems.iter().delimited() {
- self.expr(&element);
+ self.expr(&element, FixupContext::NONE);
self.trailing_comma(element.is_last);
}
self.offset(-INDENT);
@@ -127,13 +179,21 @@
self.word("]");
}
- fn expr_assign(&mut self, expr: &ExprAssign) {
+ fn expr_assign(&mut self, expr: &ExprAssign, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.left,
+ false,
+ false,
+ Precedence::Assign,
+ );
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign);
+
self.outer_attrs(&expr.attrs);
self.ibox(0);
- self.expr(&expr.left);
+ self.subexpr(&expr.left, left_prec <= Precedence::Range, left_fixup);
self.word(" = ");
self.neverbreak();
- self.expr(&expr.right);
+ self.expr(&expr.right, right_fixup);
self.end();
}
@@ -148,29 +208,75 @@
self.end();
}
- fn expr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool) {
+ fn expr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.cbox(INDENT);
- self.subexpr_await(expr, beginning_of_line);
+ self.prefix_subexpr_await(expr, beginning_of_line, fixup);
self.end();
}
- fn subexpr_await(&mut self, expr: &ExprAwait, beginning_of_line: bool) {
- self.subexpr(&expr.base, beginning_of_line);
- self.zerobreak_unless_short_ident(beginning_of_line, &expr.base);
+ fn prefix_subexpr_await(
+ &mut self,
+ expr: &ExprAwait,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.base);
+
+ self.prefix_subexpr(
+ &expr.base,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
+ if !(beginning_of_line && is_short_ident(&expr.base)) {
+ self.scan_break(BreakToken {
+ no_break: self.ends_with('.').then_some(' '),
+ ..BreakToken::default()
+ });
+ }
self.word(".await");
}
- fn expr_binary(&mut self, expr: &ExprBinary) {
+ fn expr_binary(&mut self, expr: &ExprBinary, fixup: FixupContext) {
+ let binop_prec = Precedence::of_binop(&expr.op);
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.left,
+ match &expr.op {
+ BinOp::Sub(_)
+ | BinOp::Mul(_)
+ | BinOp::And(_)
+ | BinOp::Or(_)
+ | BinOp::BitAnd(_)
+ | BinOp::BitOr(_)
+ | BinOp::Shl(_)
+ | BinOp::Lt(_) => true,
+ _ => false,
+ },
+ match &expr.op {
+ BinOp::Shl(_) | BinOp::Lt(_) => true,
+ _ => false,
+ },
+ binop_prec,
+ );
+ let left_needs_group = match binop_prec {
+ Precedence::Assign => left_prec <= Precedence::Range,
+ Precedence::Compare => left_prec <= binop_prec,
+ _ => left_prec < binop_prec,
+ };
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec);
+ let right_needs_group = binop_prec != Precedence::Assign
+ && right_fixup.rightmost_subexpression_precedence(&expr.right) <= binop_prec;
+
self.outer_attrs(&expr.attrs);
self.ibox(INDENT);
self.ibox(-INDENT);
- self.expr(&expr.left);
+ self.subexpr(&expr.left, left_needs_group, left_fixup);
self.end();
self.space();
self.binary_operator(&expr.op);
self.nbsp();
- self.expr(&expr.right);
+ self.subexpr(&expr.right, right_needs_group, right_fixup);
self.end();
}
@@ -184,7 +290,7 @@
self.end();
}
- fn expr_break(&mut self, expr: &ExprBreak) {
+ fn expr_break(&mut self, expr: &ExprBreak, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.word("break");
if let Some(lifetime) = &expr.label {
@@ -193,31 +299,62 @@
}
if let Some(value) = &expr.expr {
self.nbsp();
- self.expr(value);
+ self.subexpr(
+ value,
+ expr.label.is_none() && classify::expr_leading_label(value),
+ fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump),
+ );
}
}
- fn expr_call(&mut self, expr: &ExprCall, beginning_of_line: bool) {
+ fn expr_call(&mut self, expr: &ExprCall, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.func,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+ let needs_paren = if let Expr::Field(func) = &*expr.func {
+ matches!(func.member, Member::Named(_))
+ } else {
+ left_prec < Precedence::Unambiguous
+ };
+
self.outer_attrs(&expr.attrs);
- self.expr_beginning_of_line(&expr.func, beginning_of_line);
+ self.expr_beginning_of_line(&expr.func, needs_paren, beginning_of_line, left_fixup);
self.word("(");
self.call_args(&expr.args);
self.word(")");
}
- fn subexpr_call(&mut self, expr: &ExprCall) {
+ fn prefix_subexpr_call(&mut self, expr: &ExprCall, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.func,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+ let needs_paren = if let Expr::Field(func) = &*expr.func {
+ matches!(func.member, Member::Named(_))
+ } else {
+ left_prec < Precedence::Unambiguous
+ };
+
let beginning_of_line = false;
- self.subexpr(&expr.func, beginning_of_line);
+ self.prefix_subexpr(&expr.func, needs_paren, beginning_of_line, left_fixup);
self.word("(");
self.call_args(&expr.args);
self.word(")");
}
- fn expr_cast(&mut self, expr: &ExprCast) {
+ fn expr_cast(&mut self, expr: &ExprCast, fixup: FixupContext) {
+ let (left_prec, left_fixup) =
+ fixup.leftmost_subexpression_with_operator(&expr.expr, false, false, Precedence::Cast);
+
self.outer_attrs(&expr.attrs);
self.ibox(INDENT);
self.ibox(-INDENT);
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, left_prec < Precedence::Cast, left_fixup);
self.end();
self.space();
self.word("as ");
@@ -225,7 +362,7 @@
self.end();
}
- fn expr_closure(&mut self, expr: &ExprClosure) {
+ fn expr_closure(&mut self, expr: &ExprClosure, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.ibox(0);
if let Some(bound_lifetimes) = &expr.lifetimes {
@@ -275,16 +412,22 @@
pre_break: Some(if okay_to_brace { '{' } else { '(' }),
..BreakToken::default()
});
- self.expr(&expr.body);
+ self.expr(
+ &expr.body,
+ fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
+ );
self.scan_break(BreakToken {
offset: -INDENT,
- pre_break: (okay_to_brace && stmt::add_semi(&expr.body)).then(|| ';'),
- post_break: Some(if okay_to_brace { '}' } else { ')' }),
+ pre_break: (okay_to_brace && stmt::add_semi(&expr.body)).then_some(';'),
+ post_break: if okay_to_brace { "}" } else { ")" },
..BreakToken::default()
});
self.end();
} else {
- self.expr(&expr.body);
+ self.expr(
+ &expr.body,
+ fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
+ );
}
}
ReturnType::Type(_arrow, ty) => {
@@ -298,7 +441,17 @@
self.ty(ty);
self.nbsp();
self.neverbreak();
- self.expr(&expr.body);
+ if matches!(&*expr.body, Expr::Block(body) if body.attrs.is_empty() && body.label.is_none())
+ {
+ self.expr(
+ &expr.body,
+ fixup.rightmost_subexpression_fixup(false, false, Precedence::Jump),
+ );
+ } else {
+ self.cbox(INDENT);
+ self.expr_as_small_block(&expr.body, 0);
+ self.end();
+ }
}
}
self.end();
@@ -321,16 +474,33 @@
}
}
- fn expr_field(&mut self, expr: &ExprField, beginning_of_line: bool) {
+ fn expr_field(&mut self, expr: &ExprField, beginning_of_line: bool, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.cbox(INDENT);
- self.subexpr_field(expr, beginning_of_line);
+ self.prefix_subexpr_field(expr, beginning_of_line, fixup);
self.end();
}
- fn subexpr_field(&mut self, expr: &ExprField, beginning_of_line: bool) {
- self.subexpr(&expr.base, beginning_of_line);
- self.zerobreak_unless_short_ident(beginning_of_line, &expr.base);
+ fn prefix_subexpr_field(
+ &mut self,
+ expr: &ExprField,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.base);
+
+ self.prefix_subexpr(
+ &expr.base,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
+ if !(beginning_of_line && is_short_ident(&expr.base)) {
+ self.scan_break(BreakToken {
+ no_break: self.ends_with('.').then_some(' '),
+ ..BreakToken::default()
+ });
+ }
self.word(".");
self.member(&expr.member);
}
@@ -345,14 +515,14 @@
self.pat(&expr.pat);
self.word(" in ");
self.neverbreak();
- self.wrap_exterior_struct(&expr.expr);
+ self.expr_condition(&expr.expr);
self.word("{");
self.neverbreak();
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&expr.attrs);
- for stmt in &expr.body.stmts {
- self.stmt(stmt);
+ for stmt in expr.body.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -360,9 +530,9 @@
self.end();
}
- fn expr_group(&mut self, expr: &ExprGroup) {
+ fn expr_group(&mut self, expr: &ExprGroup, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
- self.expr(&expr.expr);
+ self.expr(&expr.expr, fixup);
}
fn expr_if(&mut self, expr: &ExprIf) {
@@ -370,7 +540,7 @@
self.cbox(INDENT);
self.word("if ");
self.cbox(-INDENT);
- self.wrap_exterior_struct(&expr.cond);
+ self.expr_condition(&expr.cond);
self.end();
if let Some((_else_token, else_branch)) = &expr.else_branch {
let mut else_branch = &**else_branch;
@@ -381,7 +551,7 @@
Expr::If(expr) => {
self.word("if ");
self.cbox(-INDENT);
- self.wrap_exterior_struct(&expr.cond);
+ self.expr_condition(&expr.cond);
self.end();
self.small_block(&expr.then_branch, &[]);
if let Some((_else_token, next)) = &expr.else_branch {
@@ -394,16 +564,7 @@
}
// If not one of the valid expressions to exist in an else
// clause, wrap in a block.
- other => {
- self.word("{");
- self.space();
- self.ibox(INDENT);
- self.expr(other);
- self.end();
- self.space();
- self.offset(-INDENT);
- self.word("}");
- }
+ other => self.expr_as_small_block(other, INDENT),
}
break;
}
@@ -412,8 +573,8 @@
} else {
self.word("{");
self.hardbreak();
- for stmt in &expr.then_branch.stmts {
- self.stmt(stmt);
+ for stmt in expr.then_branch.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.word("}");
@@ -421,18 +582,47 @@
self.end();
}
- fn expr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool) {
+ fn expr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.expr,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+
self.outer_attrs(&expr.attrs);
- self.expr_beginning_of_line(&expr.expr, beginning_of_line);
+ self.expr_beginning_of_line(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("[");
- self.expr(&expr.index);
+ self.expr(&expr.index, FixupContext::NONE);
self.word("]");
}
- fn subexpr_index(&mut self, expr: &ExprIndex, beginning_of_line: bool) {
- self.subexpr(&expr.expr, beginning_of_line);
+ fn prefix_subexpr_index(
+ &mut self,
+ expr: &ExprIndex,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_operator(
+ &expr.expr,
+ true,
+ false,
+ Precedence::Unambiguous,
+ );
+
+ self.prefix_subexpr(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("[");
- self.expr(&expr.index);
+ self.expr(&expr.index, FixupContext::NONE);
self.word("]");
}
@@ -441,7 +631,9 @@
self.word("_");
}
- fn expr_let(&mut self, expr: &ExprLet) {
+ fn expr_let(&mut self, expr: &ExprLet, fixup: FixupContext) {
+ let (right_prec, right_fixup) = fixup.rightmost_subexpression(&expr.expr, Precedence::Let);
+
self.outer_attrs(&expr.attrs);
self.ibox(0);
self.word("let ");
@@ -451,14 +643,7 @@
self.word(" = ");
self.neverbreak();
self.ibox(0);
- let needs_paren = contains_exterior_struct_lit(&expr.expr);
- if needs_paren {
- self.word("(");
- }
- self.expr(&expr.expr);
- if needs_paren {
- self.word(")");
- }
+ self.subexpr(&expr.expr, right_prec < Precedence::Let, right_fixup);
self.end();
self.end();
}
@@ -477,8 +662,8 @@
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&expr.attrs);
- for stmt in &expr.body.stmts {
- self.stmt(stmt);
+ for stmt in expr.body.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -495,7 +680,7 @@
self.outer_attrs(&expr.attrs);
self.ibox(0);
self.word("match ");
- self.wrap_exterior_struct(&expr.expr);
+ self.expr_condition(&expr.expr);
self.word("{");
self.neverbreak();
self.cbox(INDENT);
@@ -511,22 +696,40 @@
self.end();
}
- fn expr_method_call(&mut self, expr: &ExprMethodCall, beginning_of_line: bool) {
+ fn expr_method_call(
+ &mut self,
+ expr: &ExprMethodCall,
+ beginning_of_line: bool,
+ fixup: FixupContext,
+ ) {
self.outer_attrs(&expr.attrs);
self.cbox(INDENT);
let unindent_call_args = beginning_of_line && is_short_ident(&expr.receiver);
- self.subexpr_method_call(expr, beginning_of_line, unindent_call_args);
+ self.prefix_subexpr_method_call(expr, beginning_of_line, unindent_call_args, fixup);
self.end();
}
- fn subexpr_method_call(
+ fn prefix_subexpr_method_call(
&mut self,
expr: &ExprMethodCall,
beginning_of_line: bool,
unindent_call_args: bool,
+ fixup: FixupContext,
) {
- self.subexpr(&expr.receiver, beginning_of_line);
- self.zerobreak_unless_short_ident(beginning_of_line, &expr.receiver);
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.receiver);
+
+ self.prefix_subexpr(
+ &expr.receiver,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
+ if !(beginning_of_line && is_short_ident(&expr.receiver)) {
+ self.scan_break(BreakToken {
+ no_break: self.ends_with('.').then_some(' '),
+ ..BreakToken::default()
+ });
+ }
self.word(".");
self.ident(&expr.method);
if let Some(turbofish) = &expr.turbofish {
@@ -542,7 +745,7 @@
fn expr_paren(&mut self, expr: &ExprParen) {
self.outer_attrs(&expr.attrs);
self.word("(");
- self.expr(&expr.expr);
+ self.expr(&expr.expr, FixupContext::NONE);
self.word(")");
}
@@ -551,52 +754,67 @@
self.qpath(&expr.qself, &expr.path, PathKind::Expr);
}
- pub fn expr_range(&mut self, expr: &ExprRange) {
+ pub fn expr_range(&mut self, expr: &ExprRange, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
if let Some(start) = &expr.start {
- self.expr(start);
+ let (left_prec, left_fixup) =
+ fixup.leftmost_subexpression_with_operator(start, true, false, Precedence::Range);
+ self.subexpr(start, left_prec <= Precedence::Range, left_fixup);
+ } else if self.ends_with('.') {
+ self.nbsp();
}
self.word(match expr.limits {
RangeLimits::HalfOpen(_) => "..",
RangeLimits::Closed(_) => "..=",
});
if let Some(end) = &expr.end {
- self.expr(end);
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
+ let right_prec = right_fixup.rightmost_subexpression_precedence(end);
+ self.subexpr(end, right_prec <= Precedence::Range, right_fixup);
}
}
- fn expr_raw_addr(&mut self, expr: &ExprRawAddr) {
+ fn expr_raw_addr(&mut self, expr: &ExprRawAddr, fixup: FixupContext) {
+ let (right_prec, right_fixup) =
+ fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix);
+
self.outer_attrs(&expr.attrs);
self.word("&raw ");
self.pointer_mutability(&expr.mutability);
self.nbsp();
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup);
}
- fn expr_reference(&mut self, expr: &ExprReference) {
+ fn expr_reference(&mut self, expr: &ExprReference, fixup: FixupContext) {
+ let (right_prec, right_fixup) =
+ fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix);
+
self.outer_attrs(&expr.attrs);
self.word("&");
if expr.mutability.is_some() {
self.word("mut ");
}
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup);
}
fn expr_repeat(&mut self, expr: &ExprRepeat) {
self.outer_attrs(&expr.attrs);
self.word("[");
- self.expr(&expr.expr);
+ self.expr(&expr.expr, FixupContext::NONE);
self.word("; ");
- self.expr(&expr.len);
+ self.expr(&expr.len, FixupContext::NONE);
self.word("]");
}
- fn expr_return(&mut self, expr: &ExprReturn) {
+ fn expr_return(&mut self, expr: &ExprReturn, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.word("return");
if let Some(value) = &expr.expr {
self.nbsp();
- self.expr(value);
+ self.expr(
+ value,
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
+ );
}
}
@@ -614,7 +832,7 @@
}
if let Some(rest) = &expr.rest {
self.word("..");
- self.expr(rest);
+ self.expr(rest, FixupContext::NONE);
self.space();
}
self.offset(-INDENT);
@@ -622,14 +840,28 @@
self.word("}");
}
- fn expr_try(&mut self, expr: &ExprTry, beginning_of_line: bool) {
+ fn expr_try(&mut self, expr: &ExprTry, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.expr);
+
self.outer_attrs(&expr.attrs);
- self.expr_beginning_of_line(&expr.expr, beginning_of_line);
+ self.expr_beginning_of_line(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("?");
}
- fn subexpr_try(&mut self, expr: &ExprTry, beginning_of_line: bool) {
- self.subexpr(&expr.expr, beginning_of_line);
+ fn prefix_subexpr_try(&mut self, expr: &ExprTry, beginning_of_line: bool, fixup: FixupContext) {
+ let (left_prec, left_fixup) = fixup.leftmost_subexpression_with_dot(&expr.expr);
+
+ self.prefix_subexpr(
+ &expr.expr,
+ left_prec < Precedence::Unambiguous,
+ beginning_of_line,
+ left_fixup,
+ );
self.word("?");
}
@@ -647,7 +879,7 @@
self.cbox(INDENT);
self.zerobreak();
for elem in expr.elems.iter().delimited() {
- self.expr(&elem);
+ self.expr(&elem, FixupContext::NONE);
if expr.elems.len() == 1 {
self.word(",");
self.zerobreak();
@@ -660,10 +892,13 @@
self.word(")");
}
- fn expr_unary(&mut self, expr: &ExprUnary) {
+ fn expr_unary(&mut self, expr: &ExprUnary, fixup: FixupContext) {
+ let (right_prec, right_fixup) =
+ fixup.rightmost_subexpression(&expr.expr, Precedence::Prefix);
+
self.outer_attrs(&expr.attrs);
self.unary_operator(&expr.op);
- self.expr(&expr.expr);
+ self.subexpr(&expr.expr, right_prec < Precedence::Prefix, right_fixup);
}
fn expr_unsafe(&mut self, expr: &ExprUnsafe) {
@@ -675,14 +910,14 @@
}
#[cfg(not(feature = "verbatim"))]
- fn expr_verbatim(&mut self, expr: &TokenStream) {
+ fn expr_verbatim(&mut self, expr: &TokenStream, _fixup: FixupContext) {
if !expr.is_empty() {
unimplemented!("Expr::Verbatim `{}`", expr);
}
}
#[cfg(feature = "verbatim")]
- fn expr_verbatim(&mut self, tokens: &TokenStream) {
+ fn expr_verbatim(&mut self, tokens: &TokenStream, fixup: FixupContext) {
use syn::parse::discouraged::Speculative;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parenthesized, Ident};
@@ -754,7 +989,10 @@
self.outer_attrs(&expr.attrs);
self.word("become");
self.nbsp();
- self.expr(&expr.tail_call);
+ self.expr(
+ &expr.tail_call,
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
+ );
}
ExprVerbatim::Builtin(expr) => {
self.outer_attrs(&expr.attrs);
@@ -782,26 +1020,29 @@
self.label(label);
}
self.word("while ");
- self.wrap_exterior_struct(&expr.cond);
+ self.expr_condition(&expr.cond);
self.word("{");
self.neverbreak();
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&expr.attrs);
- for stmt in &expr.body.stmts {
- self.stmt(stmt);
+ for stmt in expr.body.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
self.word("}");
}
- fn expr_yield(&mut self, expr: &ExprYield) {
+ fn expr_yield(&mut self, expr: &ExprYield, fixup: FixupContext) {
self.outer_attrs(&expr.attrs);
self.word("yield");
if let Some(value) = &expr.expr {
self.nbsp();
- self.expr(value);
+ self.expr(
+ value,
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump),
+ );
}
}
@@ -816,7 +1057,7 @@
if field_value.colon_token.is_some() {
self.word(": ");
self.ibox(0);
- self.expr(&field_value.expr);
+ self.expr(&field_value.expr, FixupContext::NONE);
self.end();
}
}
@@ -827,9 +1068,9 @@
self.pat(&arm.pat);
if let Some((_if_token, guard)) = &arm.guard {
self.word(" if ");
- self.expr(guard);
+ self.expr(guard, FixupContext::NONE);
}
- self.word(" =>");
+ self.word(" => ");
let empty_block;
let mut body = &*arm.body;
while let Expr::Block(expr) = body {
@@ -856,7 +1097,6 @@
}
}
if let Expr::Block(body) = body {
- self.nbsp();
if let Some(label) = &body.label {
self.label(label);
}
@@ -865,45 +1105,44 @@
self.cbox(INDENT);
self.hardbreak_if_nonempty();
self.inner_attrs(&body.attrs);
- for stmt in &body.block.stmts {
- self.stmt(stmt);
+ for stmt in body.block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
self.word("}");
- self.end();
} else {
- self.nbsp();
self.neverbreak();
self.cbox(INDENT);
+ let okay_to_brace = parseable_as_stmt(body);
self.scan_break(BreakToken {
- pre_break: Some('{'),
+ pre_break: Some(if okay_to_brace { '{' } else { '(' }),
..BreakToken::default()
});
- self.expr_beginning_of_line(body, true);
+ self.expr_beginning_of_line(body, false, true, FixupContext::new_match_arm());
self.scan_break(BreakToken {
offset: -INDENT,
- pre_break: stmt::add_semi(body).then(|| ';'),
- post_break: Some('}'),
- no_break: requires_terminator(body).then(|| ','),
+ pre_break: (okay_to_brace && stmt::add_semi(body)).then_some(';'),
+ post_break: if okay_to_brace { "}" } else { ")," },
+ no_break: classify::requires_comma_to_be_match_arm(body).then_some(','),
..BreakToken::default()
});
self.end();
- self.end();
}
+ self.end();
}
fn call_args(&mut self, args: &Punctuated<Expr, Token![,]>) {
let mut iter = args.iter();
match (iter.next(), iter.next()) {
(Some(expr), None) if is_blocklike(expr) => {
- self.expr(expr);
+ self.expr(expr, FixupContext::NONE);
}
_ => {
self.cbox(INDENT);
self.zerobreak();
for arg in args.iter().delimited() {
- self.expr(&arg);
+ self.expr(&arg, FixupContext::NONE);
self.trailing_comma(arg.is_last);
}
self.offset(-INDENT);
@@ -920,13 +1159,13 @@
match block.stmts.as_slice() {
[Stmt::Expr(expr, None)] if stmt::break_after(expr) => {
self.ibox(0);
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
self.end();
self.space();
}
_ => {
- for stmt in &block.stmts {
- self.stmt(stmt);
+ for stmt in block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
}
}
@@ -935,6 +1174,17 @@
self.word("}");
}
+ pub fn expr_as_small_block(&mut self, expr: &Expr, indent: isize) {
+ self.word("{");
+ self.space();
+ self.ibox(indent);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
+ self.end();
+ self.space();
+ self.offset(-INDENT);
+ self.word("}");
+ }
+
pub fn member(&mut self, member: &Member) {
match member {
Member::Named(ident) => self.ident(ident),
@@ -1001,123 +1251,6 @@
PointerMutability::Mut(_) => self.word("mut"),
}
}
-
- fn zerobreak_unless_short_ident(&mut self, beginning_of_line: bool, expr: &Expr) {
- if beginning_of_line && is_short_ident(expr) {
- return;
- }
- self.zerobreak();
- }
-}
-
-fn requires_terminator(expr: &Expr) -> bool {
- // see https://github.com/rust-lang/rust/blob/a266f1199/compiler/rustc_ast/src/util/classify.rs#L7-L26
- match expr {
- #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
- Expr::If(_)
- | Expr::Match(_)
- | Expr::Block(_) | Expr::Unsafe(_) // both under ExprKind::Block in rustc
- | Expr::While(_)
- | Expr::Loop(_)
- | Expr::ForLoop(_)
- | Expr::TryBlock(_)
- | Expr::Const(_) => false,
-
- Expr::Array(_)
- | Expr::Assign(_)
- | Expr::Async(_)
- | Expr::Await(_)
- | Expr::Binary(_)
- | Expr::Break(_)
- | Expr::Call(_)
- | Expr::Cast(_)
- | Expr::Closure(_)
- | Expr::Continue(_)
- | Expr::Field(_)
- | Expr::Group(_)
- | Expr::Index(_)
- | Expr::Infer(_)
- | Expr::Let(_)
- | Expr::Lit(_)
- | Expr::Macro(_)
- | Expr::MethodCall(_)
- | Expr::Paren(_)
- | Expr::Path(_)
- | Expr::Range(_)
- | Expr::RawAddr(_)
- | Expr::Reference(_)
- | Expr::Repeat(_)
- | Expr::Return(_)
- | Expr::Struct(_)
- | Expr::Try(_)
- | Expr::Tuple(_)
- | Expr::Unary(_)
- | Expr::Verbatim(_)
- | Expr::Yield(_) => true,
-
- _ => true,
- }
-}
-
-// Expressions that syntactically contain an "exterior" struct literal i.e. not
-// surrounded by any parens or other delimiters. For example `X { y: 1 }`, `X {
-// y: 1 }.method()`, `foo == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X
-// { y: 1 }) == foo` does not.
-fn contains_exterior_struct_lit(expr: &Expr) -> bool {
- match expr {
- #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
- Expr::Struct(_) => true,
-
- Expr::Assign(ExprAssign { left, right, .. })
- | Expr::Binary(ExprBinary { left, right, .. }) => {
- // X { y: 1 } + X { y: 2 }
- contains_exterior_struct_lit(left) || contains_exterior_struct_lit(right)
- }
-
- Expr::Await(ExprAwait { base: e, .. })
- | Expr::Cast(ExprCast { expr: e, .. })
- | Expr::Field(ExprField { base: e, .. })
- | Expr::Group(ExprGroup { expr: e, .. })
- | Expr::Index(ExprIndex { expr: e, .. })
- | Expr::MethodCall(ExprMethodCall { receiver: e, .. })
- | Expr::RawAddr(ExprRawAddr { expr: e, .. })
- | Expr::Reference(ExprReference { expr: e, .. })
- | Expr::Unary(ExprUnary { expr: e, .. }) => {
- // &X { y: 1 }, X { y: 1 }.y
- contains_exterior_struct_lit(e)
- }
-
- Expr::Array(_)
- | Expr::Async(_)
- | Expr::Block(_)
- | Expr::Break(_)
- | Expr::Call(_)
- | Expr::Closure(_)
- | Expr::Const(_)
- | Expr::Continue(_)
- | Expr::ForLoop(_)
- | Expr::If(_)
- | Expr::Infer(_)
- | Expr::Let(_)
- | Expr::Lit(_)
- | Expr::Loop(_)
- | Expr::Macro(_)
- | Expr::Match(_)
- | Expr::Paren(_)
- | Expr::Path(_)
- | Expr::Range(_)
- | Expr::Repeat(_)
- | Expr::Return(_)
- | Expr::Try(_)
- | Expr::TryBlock(_)
- | Expr::Tuple(_)
- | Expr::Unsafe(_)
- | Expr::Verbatim(_)
- | Expr::While(_)
- | Expr::Yield(_) => false,
-
- _ => false,
- }
}
fn needs_newline_if_wrap(expr: &Expr) -> bool {
@@ -1208,7 +1341,6 @@
| Expr::Continue(_)
| Expr::Field(_)
| Expr::ForLoop(_)
- | Expr::Group(_)
| Expr::If(_)
| Expr::Index(_)
| Expr::Infer(_)
@@ -1231,62 +1363,106 @@
| Expr::While(_)
| Expr::Yield(_) => false,
+ Expr::Group(e) => is_blocklike(&e.expr),
+
_ => false,
}
}
+pub fn simple_block(expr: &Expr) -> Option<&ExprBlock> {
+ if let Expr::Block(expr) = expr {
+ if expr.attrs.is_empty() && expr.label.is_none() {
+ return Some(expr);
+ }
+ }
+ None
+}
+
// Expressions for which `$expr` and `{ $expr }` mean the same thing.
//
// This is not the case for all expressions. For example `{} | x | x` has some
// bitwise OR operators while `{ {} |x| x }` has a block followed by a closure.
-fn parseable_as_stmt(expr: &Expr) -> bool {
- match expr {
- #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
- Expr::Array(_)
- | Expr::Async(_)
- | Expr::Block(_)
- | Expr::Break(_)
- | Expr::Closure(_)
- | Expr::Const(_)
- | Expr::Continue(_)
- | Expr::ForLoop(_)
- | Expr::If(_)
- | Expr::Infer(_)
- | Expr::Let(_)
- | Expr::Lit(_)
- | Expr::Loop(_)
- | Expr::Macro(_)
- | Expr::Match(_)
- | Expr::Paren(_)
- | Expr::Path(_)
- | Expr::RawAddr(_)
- | Expr::Reference(_)
- | Expr::Repeat(_)
- | Expr::Return(_)
- | Expr::Struct(_)
- | Expr::TryBlock(_)
- | Expr::Tuple(_)
- | Expr::Unary(_)
- | Expr::Unsafe(_)
- | Expr::Verbatim(_)
- | Expr::While(_)
- | Expr::Yield(_) => true,
+fn parseable_as_stmt(mut expr: &Expr) -> bool {
+ loop {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Array(_)
+ | Expr::Async(_)
+ | Expr::Block(_)
+ | Expr::Break(_)
+ | Expr::Closure(_)
+ | Expr::Const(_)
+ | Expr::Continue(_)
+ | Expr::ForLoop(_)
+ | Expr::If(_)
+ | Expr::Infer(_)
+ | Expr::Lit(_)
+ | Expr::Loop(_)
+ | Expr::Macro(_)
+ | Expr::Match(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::RawAddr(_)
+ | Expr::Reference(_)
+ | Expr::Repeat(_)
+ | Expr::Return(_)
+ | Expr::Struct(_)
+ | Expr::TryBlock(_)
+ | Expr::Tuple(_)
+ | Expr::Unary(_)
+ | Expr::Unsafe(_)
+ | Expr::Verbatim(_)
+ | Expr::While(_)
+ | Expr::Yield(_) => return true,
- Expr::Assign(expr) => parseable_as_stmt(&expr.left),
- Expr::Await(expr) => parseable_as_stmt(&expr.base),
- Expr::Binary(expr) => requires_terminator(&expr.left) && parseable_as_stmt(&expr.left),
- Expr::Call(expr) => requires_terminator(&expr.func) && parseable_as_stmt(&expr.func),
- Expr::Cast(expr) => requires_terminator(&expr.expr) && parseable_as_stmt(&expr.expr),
- Expr::Field(expr) => parseable_as_stmt(&expr.base),
- Expr::Group(expr) => parseable_as_stmt(&expr.expr),
- Expr::Index(expr) => requires_terminator(&expr.expr) && parseable_as_stmt(&expr.expr),
- Expr::MethodCall(expr) => parseable_as_stmt(&expr.receiver),
- Expr::Range(expr) => match &expr.start {
- None => true,
- Some(start) => requires_terminator(start) && parseable_as_stmt(start),
- },
- Expr::Try(expr) => parseable_as_stmt(&expr.expr),
+ Expr::Let(_) => return false,
- _ => false,
+ Expr::Assign(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.left) {
+ return false;
+ }
+ expr = &e.left;
+ }
+ Expr::Await(e) => expr = &e.base,
+ Expr::Binary(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.left) {
+ return false;
+ }
+ expr = &e.left;
+ }
+ Expr::Call(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.func) {
+ return false;
+ }
+ expr = &e.func;
+ }
+ Expr::Cast(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.expr) {
+ return false;
+ }
+ expr = &e.expr;
+ }
+ Expr::Field(e) => expr = &e.base,
+ Expr::Group(e) => expr = &e.expr,
+ Expr::Index(e) => {
+ if !classify::requires_semi_to_be_stmt(&e.expr) {
+ return false;
+ }
+ expr = &e.expr;
+ }
+ Expr::MethodCall(e) => expr = &e.receiver,
+ Expr::Range(e) => match &e.start {
+ None => return true,
+ Some(start) => {
+ if !classify::requires_semi_to_be_stmt(start) {
+ return false;
+ }
+ expr = start;
+ }
+ },
+ Expr::Try(e) => expr = &e.expr,
+
+ _ => return false,
+ }
}
}
diff --git a/crates/prettyplease/src/fixup.rs b/crates/prettyplease/src/fixup.rs
new file mode 100644
index 0000000..55d3623
--- /dev/null
+++ b/crates/prettyplease/src/fixup.rs
@@ -0,0 +1,673 @@
+use crate::classify;
+use crate::precedence::Precedence;
+use syn::{
+ Expr, ExprBreak, ExprRange, ExprRawAddr, ExprReference, ExprReturn, ExprUnary, ExprYield,
+};
+
+#[derive(Copy, Clone)]
+pub struct FixupContext {
+ previous_operator: Precedence,
+ next_operator: Precedence,
+
+ // Print expression such that it can be parsed back as a statement
+ // consisting of the original expression.
+ //
+ // The effect of this is for binary operators in statement position to set
+ // `leftmost_subexpression_in_stmt` when printing their left-hand operand.
+ //
+ // (match x {}) - 1; // match needs parens when LHS of binary operator
+ //
+ // match x {}; // not when its own statement
+ //
+ stmt: bool,
+
+ // This is the difference between:
+ //
+ // (match x {}) - 1; // subexpression needs parens
+ //
+ // let _ = match x {} - 1; // no parens
+ //
+ // There are 3 distinguishable contexts in which `print_expr` might be
+ // called with the expression `$match` as its argument, where `$match`
+ // represents an expression of kind `ExprKind::Match`:
+ //
+ // - stmt=false leftmost_subexpression_in_stmt=false
+ //
+ // Example: `let _ = $match - 1;`
+ //
+ // No parentheses required.
+ //
+ // - stmt=false leftmost_subexpression_in_stmt=true
+ //
+ // Example: `$match - 1;`
+ //
+ // Must parenthesize `($match)`, otherwise parsing back the output as a
+ // statement would terminate the statement after the closing brace of
+ // the match, parsing `-1;` as a separate statement.
+ //
+ // - stmt=true leftmost_subexpression_in_stmt=false
+ //
+ // Example: `$match;`
+ //
+ // No parentheses required.
+ leftmost_subexpression_in_stmt: bool,
+
+ // Print expression such that it can be parsed as a match arm.
+ //
+ // This is almost equivalent to `stmt`, but the grammar diverges a tiny bit
+ // between statements and match arms when it comes to braced macro calls.
+ // Macro calls with brace delimiter terminate a statement without a
+ // semicolon, but do not terminate a match-arm without comma.
+ //
+ // m! {} - 1; // two statements: a macro call followed by -1 literal
+ //
+ // match () {
+ // _ => m! {} - 1, // binary subtraction operator
+ // }
+ //
+ match_arm: bool,
+
+ // This is almost equivalent to `leftmost_subexpression_in_stmt`, other than
+ // for braced macro calls.
+ //
+ // If we have `m! {} - 1` as an expression, the leftmost subexpression
+ // `m! {}` will need to be parenthesized in the statement case but not the
+ // match-arm case.
+ //
+ // (m! {}) - 1; // subexpression needs parens
+ //
+ // match () {
+ // _ => m! {} - 1, // no parens
+ // }
+ //
+ leftmost_subexpression_in_match_arm: bool,
+
+ // This is the difference between:
+ //
+ // if let _ = (Struct {}) {} // needs parens
+ //
+ // match () {
+ // () if let _ = Struct {} => {} // no parens
+ // }
+ //
+ condition: bool,
+
+ // This is the difference between:
+ //
+ // if break Struct {} == (break) {} // needs parens
+ //
+ // if break break == Struct {} {} // no parens
+ //
+ rightmost_subexpression_in_condition: bool,
+
+ // This is the difference between:
+ //
+ // if break ({ x }).field + 1 {} needs parens
+ //
+ // if break 1 + { x }.field {} // no parens
+ //
+ leftmost_subexpression_in_optional_operand: bool,
+
+ // This is the difference between:
+ //
+ // let _ = (return) - 1; // without paren, this would return -1
+ //
+ // let _ = return + 1; // no paren because '+' cannot begin expr
+ //
+ next_operator_can_begin_expr: bool,
+
+ // This is the difference between:
+ //
+ // let _ = 1 + return 1; // no parens if rightmost subexpression
+ //
+ // let _ = 1 + (return 1) + 1; // needs parens
+ //
+ next_operator_can_continue_expr: bool,
+
+ // This is the difference between:
+ //
+ // let _ = x as u8 + T;
+ //
+ // let _ = (x as u8) < T;
+ //
+ // Without parens, the latter would want to parse `u8<T...` as a type.
+ next_operator_can_begin_generics: bool,
+}
+
+impl FixupContext {
+ /// The default amount of fixing is minimal fixing. Fixups should be turned
+ /// on in a targeted fashion where needed.
+ pub const NONE: Self = FixupContext {
+ previous_operator: Precedence::MIN,
+ next_operator: Precedence::MIN,
+ stmt: false,
+ leftmost_subexpression_in_stmt: false,
+ match_arm: false,
+ leftmost_subexpression_in_match_arm: false,
+ condition: false,
+ rightmost_subexpression_in_condition: false,
+ leftmost_subexpression_in_optional_operand: false,
+ next_operator_can_begin_expr: false,
+ next_operator_can_continue_expr: false,
+ next_operator_can_begin_generics: false,
+ };
+
+ /// Create the initial fixup for printing an expression in statement
+ /// position.
+ pub fn new_stmt() -> Self {
+ FixupContext {
+ stmt: true,
+ ..FixupContext::NONE
+ }
+ }
+
+ /// Create the initial fixup for printing an expression as the right-hand
+ /// side of a match arm.
+ pub fn new_match_arm() -> Self {
+ FixupContext {
+ match_arm: true,
+ ..FixupContext::NONE
+ }
+ }
+
+ /// Create the initial fixup for printing an expression as the "condition"
+ /// of an `if` or `while`. There are a few other positions which are
+ /// grammatically equivalent and also use this, such as the iterator
+ /// expression in `for` and the scrutinee in `match`.
+ pub fn new_condition() -> Self {
+ FixupContext {
+ condition: true,
+ rightmost_subexpression_in_condition: true,
+ ..FixupContext::NONE
+ }
+ }
+
+ /// Transform this fixup into the one that should apply when printing the
+ /// leftmost subexpression of the current expression.
+ ///
+ /// The leftmost subexpression is any subexpression that has the same first
+ /// token as the current expression, but has a different last token.
+ ///
+ /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a
+ /// leftmost subexpression.
+ ///
+ /// Not every expression has a leftmost subexpression. For example neither
+ /// `-$a` nor `[$a]` have one.
+ pub fn leftmost_subexpression_with_operator(
+ self,
+ expr: &Expr,
+ next_operator_can_begin_expr: bool,
+ next_operator_can_begin_generics: bool,
+ precedence: Precedence,
+ ) -> (Precedence, Self) {
+ let fixup = FixupContext {
+ next_operator: precedence,
+ stmt: false,
+ leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
+ match_arm: false,
+ leftmost_subexpression_in_match_arm: self.match_arm
+ || self.leftmost_subexpression_in_match_arm,
+ rightmost_subexpression_in_condition: false,
+ next_operator_can_begin_expr,
+ next_operator_can_continue_expr: true,
+ next_operator_can_begin_generics,
+ ..self
+ };
+
+ (fixup.leftmost_subexpression_precedence(expr), fixup)
+ }
+
+ /// Transform this fixup into the one that should apply when printing a
+ /// leftmost subexpression followed by a `.` or `?` token, which confer
+ /// different statement boundary rules compared to other leftmost
+ /// subexpressions.
+ pub fn leftmost_subexpression_with_dot(self, expr: &Expr) -> (Precedence, Self) {
+ let fixup = FixupContext {
+ next_operator: Precedence::Unambiguous,
+ stmt: self.stmt || self.leftmost_subexpression_in_stmt,
+ leftmost_subexpression_in_stmt: false,
+ match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
+ leftmost_subexpression_in_match_arm: false,
+ rightmost_subexpression_in_condition: false,
+ next_operator_can_begin_expr: false,
+ next_operator_can_continue_expr: true,
+ next_operator_can_begin_generics: false,
+ ..self
+ };
+
+ (fixup.leftmost_subexpression_precedence(expr), fixup)
+ }
+
+ fn leftmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
+ if !self.next_operator_can_begin_expr || self.next_operator == Precedence::Range {
+ if let Scan::Bailout = scan_right(expr, self, Precedence::MIN, 0, 0) {
+ if scan_left(expr, self) {
+ return Precedence::Unambiguous;
+ }
+ }
+ }
+
+ self.precedence(expr)
+ }
+
+ /// Transform this fixup into the one that should apply when printing the
+ /// rightmost subexpression of the current expression.
+ ///
+ /// The rightmost subexpression is any subexpression that has a different
+ /// first token than the current expression, but has the same last token.
+ ///
+ /// For example in `$a + $b` and `-$b`, the subexpression `$b` is a
+ /// rightmost subexpression.
+ ///
+ /// Not every expression has a rightmost subexpression. For example neither
+ /// `[$b]` nor `$a.f($b)` have one.
+ pub fn rightmost_subexpression(
+ self,
+ expr: &Expr,
+ precedence: Precedence,
+ ) -> (Precedence, Self) {
+ let fixup = self.rightmost_subexpression_fixup(false, false, precedence);
+ (fixup.rightmost_subexpression_precedence(expr), fixup)
+ }
+
+ pub fn rightmost_subexpression_fixup(
+ self,
+ reset_allow_struct: bool,
+ optional_operand: bool,
+ precedence: Precedence,
+ ) -> Self {
+ FixupContext {
+ previous_operator: precedence,
+ stmt: false,
+ leftmost_subexpression_in_stmt: false,
+ match_arm: false,
+ leftmost_subexpression_in_match_arm: false,
+ condition: self.condition && !reset_allow_struct,
+ leftmost_subexpression_in_optional_operand: self.condition && optional_operand,
+ ..self
+ }
+ }
+
+ pub fn rightmost_subexpression_precedence(self, expr: &Expr) -> Precedence {
+ let default_prec = self.precedence(expr);
+
+ if match self.previous_operator {
+ Precedence::Assign | Precedence::Let | Precedence::Prefix => {
+ default_prec < self.previous_operator
+ }
+ _ => default_prec <= self.previous_operator,
+ } && match self.next_operator {
+ Precedence::Range | Precedence::Or | Precedence::And => true,
+ _ => !self.next_operator_can_begin_expr,
+ } {
+ if let Scan::Bailout | Scan::Fail = scan_right(expr, self, self.previous_operator, 1, 0)
+ {
+ if scan_left(expr, self) {
+ return Precedence::Prefix;
+ }
+ }
+ }
+
+ default_prec
+ }
+
+ /// Determine whether parentheses are needed around the given expression to
+ /// head off the early termination of a statement or condition.
+ pub fn parenthesize(self, expr: &Expr) -> bool {
+ (self.leftmost_subexpression_in_stmt && !classify::requires_semi_to_be_stmt(expr))
+ || ((self.stmt || self.leftmost_subexpression_in_stmt) && matches!(expr, Expr::Let(_)))
+ || (self.leftmost_subexpression_in_match_arm
+ && !classify::requires_comma_to_be_match_arm(expr))
+ || (self.condition && matches!(expr, Expr::Struct(_)))
+ || (self.rightmost_subexpression_in_condition
+ && matches!(
+ expr,
+ Expr::Return(ExprReturn { expr: None, .. })
+ | Expr::Yield(ExprYield { expr: None, .. })
+ ))
+ || (self.rightmost_subexpression_in_condition
+ && !self.condition
+ && matches!(
+ expr,
+ Expr::Break(ExprBreak { expr: None, .. })
+ | Expr::Path(_)
+ | Expr::Range(ExprRange { end: None, .. })
+ ))
+ || (self.leftmost_subexpression_in_optional_operand
+ && matches!(expr, Expr::Block(expr) if expr.attrs.is_empty() && expr.label.is_none()))
+ }
+
+ /// Determines the effective precedence of a subexpression. Some expressions
+ /// have higher or lower precedence when adjacent to particular operators.
+ fn precedence(self, expr: &Expr) -> Precedence {
+ if self.next_operator_can_begin_expr {
+ // Decrease precedence of value-less jumps when followed by an
+ // operator that would otherwise get interpreted as beginning a
+ // value for the jump.
+ if let Expr::Break(ExprBreak { expr: None, .. })
+ | Expr::Return(ExprReturn { expr: None, .. })
+ | Expr::Yield(ExprYield { expr: None, .. }) = expr
+ {
+ return Precedence::Jump;
+ }
+ }
+
+ if !self.next_operator_can_continue_expr {
+ match expr {
+ // Increase precedence of expressions that extend to the end of
+ // current statement or group.
+ Expr::Break(_)
+ | Expr::Closure(_)
+ | Expr::Let(_)
+ | Expr::Return(_)
+ | Expr::Yield(_) => {
+ return Precedence::Prefix;
+ }
+ Expr::Range(e) if e.start.is_none() => return Precedence::Prefix,
+ _ => {}
+ }
+ }
+
+ if self.next_operator_can_begin_generics {
+ if let Expr::Cast(cast) = expr {
+ if classify::trailing_unparameterized_path(&cast.ty) {
+ return Precedence::MIN;
+ }
+ }
+ }
+
+ Precedence::of(expr)
+ }
+}
+
+#[derive(Copy, Clone, PartialEq)]
+enum Scan {
+ Fail,
+ Bailout,
+ Consume,
+}
+
+fn scan_left(expr: &Expr, fixup: FixupContext) -> bool {
+ match expr {
+ Expr::Assign(_) => fixup.previous_operator <= Precedence::Assign,
+ Expr::Binary(e) => match Precedence::of_binop(&e.op) {
+ Precedence::Assign => fixup.previous_operator <= Precedence::Assign,
+ binop_prec => fixup.previous_operator < binop_prec,
+ },
+ Expr::Cast(_) => fixup.previous_operator < Precedence::Cast,
+ Expr::Range(e) => e.start.is_none() || fixup.previous_operator < Precedence::Assign,
+ _ => true,
+ }
+}
+
+fn scan_right(
+ expr: &Expr,
+ fixup: FixupContext,
+ precedence: Precedence,
+ fail_offset: u8,
+ bailout_offset: u8,
+) -> Scan {
+ let consume_by_precedence = if match precedence {
+ Precedence::Assign | Precedence::Compare => precedence <= fixup.next_operator,
+ _ => precedence < fixup.next_operator,
+ } || fixup.next_operator == Precedence::MIN
+ {
+ Scan::Consume
+ } else {
+ Scan::Bailout
+ };
+ if fixup.parenthesize(expr) {
+ return consume_by_precedence;
+ }
+ match expr {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Assign(e) => {
+ if match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset >= 2,
+ _ => bailout_offset >= 1,
+ } {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Assign);
+ let scan = scan_right(
+ &e.right,
+ right_fixup,
+ Precedence::Assign,
+ match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset,
+ _ => 1,
+ },
+ 1,
+ );
+ if let Scan::Bailout | Scan::Consume = scan {
+ Scan::Consume
+ } else if let Precedence::Unambiguous = fixup.next_operator {
+ Scan::Fail
+ } else {
+ Scan::Bailout
+ }
+ }
+ Expr::Binary(e) => {
+ if match fixup.next_operator {
+ Precedence::Unambiguous => {
+ fail_offset >= 2
+ && (consume_by_precedence == Scan::Consume || bailout_offset >= 1)
+ }
+ _ => bailout_offset >= 1,
+ } {
+ return Scan::Consume;
+ }
+ let binop_prec = Precedence::of_binop(&e.op);
+ if binop_prec == Precedence::Compare && fixup.next_operator == Precedence::Compare {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, binop_prec);
+ let scan = scan_right(
+ &e.right,
+ right_fixup,
+ binop_prec,
+ match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset,
+ _ => 1,
+ },
+ consume_by_precedence as u8 - Scan::Bailout as u8,
+ );
+ match scan {
+ Scan::Fail => {}
+ Scan::Bailout => return consume_by_precedence,
+ Scan::Consume => return Scan::Consume,
+ }
+ let right_needs_group = binop_prec != Precedence::Assign
+ && right_fixup.rightmost_subexpression_precedence(&e.right) <= binop_prec;
+ if right_needs_group {
+ consume_by_precedence
+ } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
+ Scan::Fail
+ } else {
+ Scan::Bailout
+ }
+ }
+ Expr::RawAddr(ExprRawAddr { expr, .. })
+ | Expr::Reference(ExprReference { expr, .. })
+ | Expr::Unary(ExprUnary { expr, .. }) => {
+ if match fixup.next_operator {
+ Precedence::Unambiguous => {
+ fail_offset >= 2
+ && (consume_by_precedence == Scan::Consume || bailout_offset >= 1)
+ }
+ _ => bailout_offset >= 1,
+ } {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Prefix);
+ let scan = scan_right(
+ expr,
+ right_fixup,
+ precedence,
+ match fixup.next_operator {
+ Precedence::Unambiguous => fail_offset,
+ _ => 1,
+ },
+ consume_by_precedence as u8 - Scan::Bailout as u8,
+ );
+ match scan {
+ Scan::Fail => {}
+ Scan::Bailout => return consume_by_precedence,
+ Scan::Consume => return Scan::Consume,
+ }
+ if right_fixup.rightmost_subexpression_precedence(expr) < Precedence::Prefix {
+ consume_by_precedence
+ } else if let (Scan::Fail, Precedence::Unambiguous) = (scan, fixup.next_operator) {
+ Scan::Fail
+ } else {
+ Scan::Bailout
+ }
+ }
+ Expr::Range(e) => match &e.end {
+ Some(end) => {
+ if fail_offset >= 2 {
+ return Scan::Consume;
+ }
+ let right_fixup =
+ fixup.rightmost_subexpression_fixup(false, true, Precedence::Range);
+ let scan = scan_right(
+ end,
+ right_fixup,
+ Precedence::Range,
+ fail_offset,
+ match fixup.next_operator {
+ Precedence::Assign | Precedence::Range => 0,
+ _ => 1,
+ },
+ );
+ if match (scan, fixup.next_operator) {
+ (Scan::Fail, _) => false,
+ (Scan::Bailout, Precedence::Assign | Precedence::Range) => false,
+ (Scan::Bailout | Scan::Consume, _) => true,
+ } {
+ return Scan::Consume;
+ }
+ if right_fixup.rightmost_subexpression_precedence(end) <= Precedence::Range {
+ Scan::Consume
+ } else {
+ Scan::Fail
+ }
+ }
+ None => {
+ if fixup.next_operator_can_begin_expr {
+ Scan::Consume
+ } else {
+ Scan::Fail
+ }
+ }
+ },
+ Expr::Break(e) => match &e.expr {
+ Some(value) => {
+ if bailout_offset >= 1 || e.label.is_none() && classify::expr_leading_label(value) {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(true, true, Precedence::Jump);
+ match scan_right(value, right_fixup, Precedence::Jump, 1, 1) {
+ Scan::Fail => Scan::Bailout,
+ Scan::Bailout | Scan::Consume => Scan::Consume,
+ }
+ }
+ None => match fixup.next_operator {
+ Precedence::Assign if precedence > Precedence::Assign => Scan::Fail,
+ _ => Scan::Consume,
+ },
+ },
+ Expr::Return(ExprReturn { expr, .. }) | Expr::Yield(ExprYield { expr, .. }) => match expr {
+ Some(e) => {
+ if bailout_offset >= 1 {
+ return Scan::Consume;
+ }
+ let right_fixup =
+ fixup.rightmost_subexpression_fixup(true, false, Precedence::Jump);
+ match scan_right(e, right_fixup, Precedence::Jump, 1, 1) {
+ Scan::Fail => Scan::Bailout,
+ Scan::Bailout | Scan::Consume => Scan::Consume,
+ }
+ }
+ None => match fixup.next_operator {
+ Precedence::Assign if precedence > Precedence::Assign => Scan::Fail,
+ _ => Scan::Consume,
+ },
+ },
+ Expr::Closure(_) => Scan::Consume,
+ Expr::Let(e) => {
+ if bailout_offset >= 1 {
+ return Scan::Consume;
+ }
+ let right_fixup = fixup.rightmost_subexpression_fixup(false, false, Precedence::Let);
+ let scan = scan_right(
+ &e.expr,
+ right_fixup,
+ Precedence::Let,
+ 1,
+ if fixup.next_operator < Precedence::Let {
+ 0
+ } else {
+ 1
+ },
+ );
+ match scan {
+ Scan::Fail | Scan::Bailout if fixup.next_operator < Precedence::Let => {
+ return Scan::Bailout;
+ }
+ Scan::Consume => return Scan::Consume,
+ _ => {}
+ }
+ if right_fixup.rightmost_subexpression_precedence(&e.expr) < Precedence::Let {
+ Scan::Consume
+ } else if let Scan::Fail = scan {
+ Scan::Bailout
+ } else {
+ Scan::Consume
+ }
+ }
+ Expr::Group(e) => scan_right(&e.expr, fixup, precedence, fail_offset, bailout_offset),
+ Expr::Array(_)
+ | Expr::Async(_)
+ | Expr::Await(_)
+ | Expr::Block(_)
+ | Expr::Call(_)
+ | Expr::Cast(_)
+ | Expr::Const(_)
+ | Expr::Continue(_)
+ | Expr::Field(_)
+ | Expr::ForLoop(_)
+ | Expr::If(_)
+ | Expr::Index(_)
+ | Expr::Infer(_)
+ | Expr::Lit(_)
+ | Expr::Loop(_)
+ | Expr::Macro(_)
+ | Expr::Match(_)
+ | Expr::MethodCall(_)
+ | Expr::Paren(_)
+ | Expr::Path(_)
+ | Expr::Repeat(_)
+ | Expr::Struct(_)
+ | Expr::Try(_)
+ | Expr::TryBlock(_)
+ | Expr::Tuple(_)
+ | Expr::Unsafe(_)
+ | Expr::Verbatim(_)
+ | Expr::While(_) => match fixup.next_operator {
+ Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail,
+ _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => {
+ Scan::Fail
+ }
+ _ => consume_by_precedence,
+ },
+
+ _ => match fixup.next_operator {
+ Precedence::Assign | Precedence::Range if precedence == Precedence::Range => Scan::Fail,
+ _ if precedence == Precedence::Let && fixup.next_operator < Precedence::Let => {
+ Scan::Fail
+ }
+ _ => consume_by_precedence,
+ },
+ }
+}
diff --git a/crates/prettyplease/src/generics.rs b/crates/prettyplease/src/generics.rs
index 6145345..3f225b8 100644
--- a/crates/prettyplease/src/generics.rs
+++ b/crates/prettyplease/src/generics.rs
@@ -5,7 +5,7 @@
use proc_macro2::TokenStream;
use std::ptr;
use syn::{
- BoundLifetimes, CapturedParam, ConstParam, GenericParam, Generics, LifetimeParam,
+ BoundLifetimes, CapturedParam, ConstParam, Expr, GenericParam, Generics, LifetimeParam,
PreciseCapture, PredicateLifetime, PredicateType, TraitBound, TraitBoundModifier, TypeParam,
TypeParamBound, WhereClause, WherePredicate,
};
@@ -209,7 +209,7 @@
self.ty(&const_param.ty);
if let Some(default) = &const_param.default {
self.word(" = ");
- self.expr(default);
+ self.const_argument(default);
}
}
@@ -357,4 +357,27 @@
_ => unimplemented!("unknown CapturedParam"),
}
}
+
+ pub fn const_argument(&mut self, expr: &Expr) {
+ match expr {
+ #![cfg_attr(all(test, exhaustive), allow(non_exhaustive_omitted_patterns))]
+ Expr::Lit(expr) => self.expr_lit(expr),
+
+ Expr::Path(expr)
+ if expr.attrs.is_empty()
+ && expr.qself.is_none()
+ && expr.path.get_ident().is_some() =>
+ {
+ self.expr_path(expr);
+ }
+
+ Expr::Block(expr) => self.expr_block(expr),
+
+ _ => {
+ self.cbox(INDENT);
+ self.expr_as_small_block(expr, 0);
+ self.end();
+ }
+ }
+ }
}
diff --git a/crates/prettyplease/src/item.rs b/crates/prettyplease/src/item.rs
index 8254429..4e9d95d 100644
--- a/crates/prettyplease/src/item.rs
+++ b/crates/prettyplease/src/item.rs
@@ -1,5 +1,7 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
+use crate::mac;
use crate::path::PathKind;
use crate::INDENT;
use proc_macro2::TokenStream;
@@ -47,7 +49,7 @@
self.ty(&item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&item.expr);
+ self.expr(&item.expr, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -100,8 +102,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&item.attrs);
- for stmt in &item.block.stmts {
- self.stmt(stmt);
+ for stmt in item.block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -168,7 +170,7 @@
fn item_macro(&mut self, item: &ItemMacro) {
self.outer_attrs(&item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&item.mac.delimiter);
self.mac(&item.mac, item.ident.as_ref(), semicolon);
self.hardbreak();
}
@@ -210,7 +212,7 @@
self.ty(&item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&item.expr);
+ self.expr(&item.expr, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -836,7 +838,7 @@
fn foreign_item_macro(&mut self, foreign_item: &ForeignItemMacro) {
self.outer_attrs(&foreign_item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&foreign_item.mac.delimiter);
self.mac(&foreign_item.mac, None, semicolon);
self.hardbreak();
}
@@ -961,7 +963,7 @@
if let Some((_eq_token, default)) = &trait_item.default {
self.word(" = ");
self.neverbreak();
- self.expr(default);
+ self.expr(default, FixupContext::NONE);
}
self.word(";");
self.end();
@@ -981,8 +983,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&trait_item.attrs);
- for stmt in &block.stmts {
- self.stmt(stmt);
+ for stmt in block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -1023,7 +1025,7 @@
fn trait_item_macro(&mut self, trait_item: &TraitItemMacro) {
self.outer_attrs(&trait_item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&trait_item.mac.delimiter);
self.mac(&trait_item.mac, None, semicolon);
self.hardbreak();
}
@@ -1158,7 +1160,7 @@
self.ty(&impl_item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&impl_item.expr);
+ self.expr(&impl_item.expr, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -1180,8 +1182,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&impl_item.attrs);
- for stmt in &impl_item.block.stmts {
- self.stmt(stmt);
+ for stmt in impl_item.block.stmts.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -1211,7 +1213,7 @@
fn impl_item_macro(&mut self, impl_item: &ImplItemMacro) {
self.outer_attrs(&impl_item.attrs);
- let semicolon = true;
+ let semicolon = mac::requires_semi(&impl_item.mac.delimiter);
self.mac(&impl_item.mac, None, semicolon);
self.hardbreak();
}
@@ -1421,6 +1423,7 @@
#[cfg(feature = "verbatim")]
mod verbatim {
use crate::algorithm::Printer;
+ use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::INDENT;
use syn::ext::IdentExt;
@@ -1707,7 +1710,7 @@
self.word(" = ");
self.neverbreak();
self.ibox(-INDENT);
- self.expr(value);
+ self.expr(value, FixupContext::NONE);
self.end();
}
self.where_clause_oneline_semi(&item.generics.where_clause);
@@ -1728,8 +1731,8 @@
self.word("{");
self.hardbreak_if_nonempty();
self.inner_attrs(&item.attrs);
- for stmt in body {
- self.stmt(stmt);
+ for stmt in body.iter().delimited() {
+ self.stmt(&stmt, stmt.is_last);
}
self.offset(-INDENT);
self.end();
@@ -1756,7 +1759,7 @@
if let Some(expr) = &item.expr {
self.word(" = ");
self.neverbreak();
- self.expr(expr);
+ self.expr(expr, FixupContext::NONE);
}
self.word(";");
self.end();
diff --git a/crates/prettyplease/src/lib.rs b/crates/prettyplease/src/lib.rs
index b970d8f..25f0250 100644
--- a/crates/prettyplease/src/lib.rs
+++ b/crates/prettyplease/src/lib.rs
@@ -320,8 +320,9 @@
//! these situations with conditional punctuation tokens whose selection can be
//! deferred and populated after it's known that the group is or is not broken.
-#![doc(html_root_url = "https://docs.rs/prettyplease/0.2.25")]
+#![doc(html_root_url = "https://docs.rs/prettyplease/0.2.29")]
#![allow(
+ clippy::bool_to_int_with_if,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::derive_partial_eq_without_eq,
@@ -336,6 +337,7 @@
clippy::needless_pass_by_value,
clippy::ref_option,
clippy::similar_names,
+ clippy::struct_excessive_bools,
clippy::too_many_lines,
clippy::unused_self,
clippy::vec_init_then_push
@@ -344,10 +346,12 @@
mod algorithm;
mod attr;
+mod classify;
mod convenience;
mod data;
mod expr;
mod file;
+mod fixup;
mod generics;
mod item;
mod iter;
@@ -356,6 +360,7 @@
mod mac;
mod pat;
mod path;
+mod precedence;
mod ring;
mod stmt;
mod token;
diff --git a/crates/prettyplease/src/mac.rs b/crates/prettyplease/src/mac.rs
index ebc56c7..230a8d4 100644
--- a/crates/prettyplease/src/mac.rs
+++ b/crates/prettyplease/src/mac.rs
@@ -41,10 +41,7 @@
}
self.word(close);
if semicolon {
- match mac.delimiter {
- MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => self.word(";"),
- MacroDelimiter::Brace(_) => {}
- }
+ self.word(";");
}
}
@@ -179,6 +176,7 @@
(_, Token::Ident(ident)) if !is_keyword(ident) => {
(state != Dot && state != Colon2, Ident)
}
+ (_, Token::Literal(lit)) if lit.to_string().ends_with('.') => (state != Dot, Other),
(_, Token::Literal(_)) => (state != Dot, Ident),
(_, Token::Punct(',' | ';', _)) => (false, Other),
(_, Token::Punct('.', _)) if !matcher => (state != Ident && state != Delim, Dot),
@@ -211,6 +209,13 @@
}
}
+pub(crate) fn requires_semi(delimiter: &MacroDelimiter) -> bool {
+ match delimiter {
+ MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => true,
+ MacroDelimiter::Brace(_) => false,
+ }
+}
+
fn is_keyword(ident: &Ident) -> bool {
match ident.to_string().as_str() {
"as" | "async" | "await" | "box" | "break" | "const" | "continue" | "crate" | "dyn"
@@ -224,6 +229,7 @@
#[cfg(feature = "verbatim")]
mod standard_library {
use crate::algorithm::Printer;
+ use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -543,7 +549,7 @@
self.word("(");
self.cbox(INDENT);
self.zerobreak();
- self.expr(expr);
+ self.expr(expr, FixupContext::NONE);
self.zerobreak();
self.offset(-INDENT);
self.end();
@@ -554,7 +560,7 @@
self.cbox(INDENT);
self.zerobreak();
for elem in exprs.iter().delimited() {
- self.expr(&elem);
+ self.expr(&elem, FixupContext::NONE);
self.trailing_comma(elem.is_last);
}
self.offset(-INDENT);
@@ -570,14 +576,14 @@
self.word("(");
self.cbox(INDENT);
self.zerobreak();
- self.expr(&matches.expression);
+ self.expr(&matches.expression, FixupContext::NONE);
self.word(",");
self.space();
self.pat(&matches.pattern);
if let Some(guard) = &matches.guard {
self.space();
self.word("if ");
- self.expr(guard);
+ self.expr(guard, FixupContext::NONE);
}
self.zerobreak();
self.offset(-INDENT);
@@ -598,7 +604,7 @@
self.ty(&item.ty);
self.word(" = ");
self.neverbreak();
- self.expr(&item.init);
+ self.expr(&item.init, FixupContext::NONE);
self.word(";");
self.end();
self.hardbreak();
@@ -613,7 +619,7 @@
self.cbox(INDENT);
self.zerobreak();
for elem in vec.iter().delimited() {
- self.expr(&elem);
+ self.expr(&elem, FixupContext::NONE);
self.trailing_comma(elem.is_last);
}
self.offset(-INDENT);
@@ -624,10 +630,10 @@
self.word("[");
self.cbox(INDENT);
self.zerobreak();
- self.expr(elem);
+ self.expr(elem, FixupContext::NONE);
self.word(";");
self.space();
- self.expr(n);
+ self.expr(n, FixupContext::NONE);
self.zerobreak();
self.offset(-INDENT);
self.end();
diff --git a/crates/prettyplease/src/pat.rs b/crates/prettyplease/src/pat.rs
index aa1806e..23a38cb 100644
--- a/crates/prettyplease/src/pat.rs
+++ b/crates/prettyplease/src/pat.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -19,7 +20,7 @@
Pat::Or(pat) => self.pat_or(pat),
Pat::Paren(pat) => self.pat_paren(pat),
Pat::Path(pat) => self.expr_path(pat),
- Pat::Range(pat) => self.expr_range(pat),
+ Pat::Range(pat) => self.expr_range(pat, FixupContext::NONE),
Pat::Reference(pat) => self.pat_reference(pat),
Pat::Rest(pat) => self.pat_rest(pat),
Pat::Slice(pat) => self.pat_slice(pat),
diff --git a/crates/prettyplease/src/path.rs b/crates/prettyplease/src/path.rs
index 2ae94b3..44428cc 100644
--- a/crates/prettyplease/src/path.rs
+++ b/crates/prettyplease/src/path.rs
@@ -3,7 +3,7 @@
use crate::INDENT;
use std::ptr;
use syn::{
- AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, Expr, GenericArgument,
+ AngleBracketedGenericArguments, AssocConst, AssocType, Constraint, GenericArgument,
ParenthesizedGenericArguments, Path, PathArguments, PathSegment, QSelf,
};
@@ -50,20 +50,7 @@
#![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
GenericArgument::Lifetime(lifetime) => self.lifetime(lifetime),
GenericArgument::Type(ty) => self.ty(ty),
- GenericArgument::Const(expr) => {
- match expr {
- #![cfg_attr(all(test, exhaustive), allow(non_exhaustive_omitted_patterns))]
- Expr::Lit(expr) => self.expr_lit(expr),
- Expr::Block(expr) => self.expr_block(expr),
- // ERROR CORRECTION: Add braces to make sure that the
- // generated code is valid.
- _ => {
- self.word("{");
- self.expr(expr);
- self.word("}");
- }
- }
- }
+ GenericArgument::Const(expr) => self.const_argument(expr),
GenericArgument::AssocType(assoc) => self.assoc_type(assoc),
GenericArgument::AssocConst(assoc) => self.assoc_const(assoc),
GenericArgument::Constraint(constraint) => self.constraint(constraint),
@@ -136,7 +123,7 @@
self.angle_bracketed_generic_arguments(generics, PathKind::Type);
}
self.word(" = ");
- self.expr(&assoc.value);
+ self.const_argument(&assoc.value);
}
fn constraint(&mut self, constraint: &Constraint) {
diff --git a/crates/prettyplease/src/precedence.rs b/crates/prettyplease/src/precedence.rs
new file mode 100644
index 0000000..03117d5
--- /dev/null
+++ b/crates/prettyplease/src/precedence.rs
@@ -0,0 +1,148 @@
+use syn::{
+ AttrStyle, Attribute, BinOp, Expr, ExprArray, ExprAsync, ExprAwait, ExprBlock, ExprBreak,
+ ExprCall, ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprInfer,
+ ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRepeat,
+ ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnsafe, ExprWhile, ExprYield,
+ ReturnType,
+};
+
+// Reference: https://doc.rust-lang.org/reference/expressions.html#expression-precedence
+#[derive(Copy, Clone, PartialEq, PartialOrd)]
+pub enum Precedence {
+ // return, break, closures
+ Jump,
+ // = += -= *= /= %= &= |= ^= <<= >>=
+ Assign,
+ // .. ..=
+ Range,
+ // ||
+ Or,
+ // &&
+ And,
+ // let
+ Let,
+ // == != < > <= >=
+ Compare,
+ // |
+ BitOr,
+ // ^
+ BitXor,
+ // &
+ BitAnd,
+ // << >>
+ Shift,
+ // + -
+ Sum,
+ // * / %
+ Product,
+ // as
+ Cast,
+ // unary - * ! & &mut
+ Prefix,
+ // paths, loops, function calls, array indexing, field expressions, method calls
+ Unambiguous,
+}
+
+impl Precedence {
+ pub(crate) const MIN: Self = Precedence::Jump;
+
+ pub(crate) fn of_binop(op: &BinOp) -> Self {
+ match op {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ BinOp::Add(_) | BinOp::Sub(_) => Precedence::Sum,
+ BinOp::Mul(_) | BinOp::Div(_) | BinOp::Rem(_) => Precedence::Product,
+ BinOp::And(_) => Precedence::And,
+ BinOp::Or(_) => Precedence::Or,
+ BinOp::BitXor(_) => Precedence::BitXor,
+ BinOp::BitAnd(_) => Precedence::BitAnd,
+ BinOp::BitOr(_) => Precedence::BitOr,
+ BinOp::Shl(_) | BinOp::Shr(_) => Precedence::Shift,
+
+ BinOp::Eq(_)
+ | BinOp::Lt(_)
+ | BinOp::Le(_)
+ | BinOp::Ne(_)
+ | BinOp::Ge(_)
+ | BinOp::Gt(_) => Precedence::Compare,
+
+ BinOp::AddAssign(_)
+ | BinOp::SubAssign(_)
+ | BinOp::MulAssign(_)
+ | BinOp::DivAssign(_)
+ | BinOp::RemAssign(_)
+ | BinOp::BitXorAssign(_)
+ | BinOp::BitAndAssign(_)
+ | BinOp::BitOrAssign(_)
+ | BinOp::ShlAssign(_)
+ | BinOp::ShrAssign(_) => Precedence::Assign,
+
+ _ => Precedence::MIN,
+ }
+ }
+
+ pub(crate) fn of(e: &Expr) -> Self {
+ fn prefix_attrs(attrs: &[Attribute]) -> Precedence {
+ for attr in attrs {
+ if let AttrStyle::Outer = attr.style {
+ return Precedence::Prefix;
+ }
+ }
+ Precedence::Unambiguous
+ }
+
+ match e {
+ #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
+ Expr::Closure(e) => match e.output {
+ ReturnType::Default => Precedence::Jump,
+ ReturnType::Type(..) => prefix_attrs(&e.attrs),
+ },
+
+ Expr::Break(ExprBreak { expr, .. })
+ | Expr::Return(ExprReturn { expr, .. })
+ | Expr::Yield(ExprYield { expr, .. }) => match expr {
+ Some(_) => Precedence::Jump,
+ None => Precedence::Unambiguous,
+ },
+
+ Expr::Assign(_) => Precedence::Assign,
+ Expr::Range(_) => Precedence::Range,
+ Expr::Binary(e) => Precedence::of_binop(&e.op),
+ Expr::Let(_) => Precedence::Let,
+ Expr::Cast(_) => Precedence::Cast,
+ Expr::RawAddr(_) | Expr::Reference(_) | Expr::Unary(_) => Precedence::Prefix,
+
+ Expr::Array(ExprArray { attrs, .. })
+ | Expr::Async(ExprAsync { attrs, .. })
+ | Expr::Await(ExprAwait { attrs, .. })
+ | Expr::Block(ExprBlock { attrs, .. })
+ | Expr::Call(ExprCall { attrs, .. })
+ | Expr::Const(ExprConst { attrs, .. })
+ | Expr::Continue(ExprContinue { attrs, .. })
+ | Expr::Field(ExprField { attrs, .. })
+ | Expr::ForLoop(ExprForLoop { attrs, .. })
+ | Expr::If(ExprIf { attrs, .. })
+ | Expr::Index(ExprIndex { attrs, .. })
+ | Expr::Infer(ExprInfer { attrs, .. })
+ | Expr::Lit(ExprLit { attrs, .. })
+ | Expr::Loop(ExprLoop { attrs, .. })
+ | Expr::Macro(ExprMacro { attrs, .. })
+ | Expr::Match(ExprMatch { attrs, .. })
+ | Expr::MethodCall(ExprMethodCall { attrs, .. })
+ | Expr::Paren(ExprParen { attrs, .. })
+ | Expr::Path(ExprPath { attrs, .. })
+ | Expr::Repeat(ExprRepeat { attrs, .. })
+ | Expr::Struct(ExprStruct { attrs, .. })
+ | Expr::Try(ExprTry { attrs, .. })
+ | Expr::TryBlock(ExprTryBlock { attrs, .. })
+ | Expr::Tuple(ExprTuple { attrs, .. })
+ | Expr::Unsafe(ExprUnsafe { attrs, .. })
+ | Expr::While(ExprWhile { attrs, .. }) => prefix_attrs(attrs),
+
+ Expr::Group(e) => Precedence::of(&e.expr),
+
+ Expr::Verbatim(_) => Precedence::Unambiguous,
+
+ _ => Precedence::Unambiguous,
+ }
+ }
+}
diff --git a/crates/prettyplease/src/ring.rs b/crates/prettyplease/src/ring.rs
index aff9258..882a988 100644
--- a/crates/prettyplease/src/ring.rs
+++ b/crates/prettyplease/src/ring.rs
@@ -1,5 +1,5 @@
use std::collections::VecDeque;
-use std::ops::{Index, IndexMut};
+use std::ops::{Index, IndexMut, Range};
pub struct RingBuffer<T> {
data: VecDeque<T>,
@@ -33,8 +33,8 @@
self.data.clear();
}
- pub fn index_of_first(&self) -> usize {
- self.offset
+ pub fn index_range(&self) -> Range<usize> {
+ self.offset..self.offset + self.data.len()
}
pub fn first(&self) -> &T {
diff --git a/crates/prettyplease/src/stmt.rs b/crates/prettyplease/src/stmt.rs
index 2b97da6..ce58200 100644
--- a/crates/prettyplease/src/stmt.rs
+++ b/crates/prettyplease/src/stmt.rs
@@ -1,9 +1,13 @@
use crate::algorithm::Printer;
+use crate::classify;
+use crate::expr;
+use crate::fixup::FixupContext;
+use crate::mac;
use crate::INDENT;
use syn::{BinOp, Expr, Stmt};
impl Printer {
- pub fn stmt(&mut self, stmt: &Stmt) {
+ pub fn stmt(&mut self, stmt: &Stmt, is_last: bool) {
match stmt {
Stmt::Local(local) => {
self.outer_attrs(&local.attrs);
@@ -13,32 +17,26 @@
if let Some(local_init) = &local.init {
self.word(" = ");
self.neverbreak();
- self.expr(&local_init.expr);
+ self.subexpr(
+ &local_init.expr,
+ local_init.diverge.is_some()
+ && classify::expr_trailing_brace(&local_init.expr),
+ FixupContext::NONE,
+ );
if let Some((_else, diverge)) = &local_init.diverge {
self.space();
self.word("else ");
self.end();
self.neverbreak();
- if let Expr::Block(expr) = diverge.as_ref() {
- self.cbox(INDENT);
+ self.cbox(INDENT);
+ if let Some(expr) = expr::simple_block(diverge) {
self.small_block(&expr.block, &[]);
- self.end();
} else {
- self.word("{");
- self.space();
- self.ibox(INDENT);
- self.expr(diverge);
- self.end();
- self.space();
- self.offset(-INDENT);
- self.word("}");
+ self.expr_as_small_block(diverge, INDENT);
}
- } else {
- self.end();
}
- } else {
- self.end();
}
+ self.end();
self.word(";");
self.hardbreak();
}
@@ -46,14 +44,14 @@
Stmt::Expr(expr, None) => {
if break_after(expr) {
self.ibox(0);
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
if add_semi(expr) {
self.word(";");
}
self.end();
self.hardbreak();
} else {
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
}
}
Stmt::Expr(expr, Some(_semi)) => {
@@ -63,7 +61,7 @@
}
}
self.ibox(0);
- self.expr_beginning_of_line(expr, true);
+ self.expr_beginning_of_line(expr, false, true, FixupContext::new_stmt());
if !remove_semi(expr) {
self.word(";");
}
@@ -72,7 +70,8 @@
}
Stmt::Macro(stmt) => {
self.outer_attrs(&stmt.attrs);
- let semicolon = true;
+ let semicolon = stmt.semi_token.is_some()
+ || !is_last && mac::requires_semi(&stmt.mac.delimiter);
self.mac(&stmt.mac, None, semicolon);
self.hardbreak();
}
diff --git a/crates/prettyplease/src/ty.rs b/crates/prettyplease/src/ty.rs
index ec2cd0a..7f92821 100644
--- a/crates/prettyplease/src/ty.rs
+++ b/crates/prettyplease/src/ty.rs
@@ -1,4 +1,5 @@
use crate::algorithm::Printer;
+use crate::fixup::FixupContext;
use crate::iter::IterDelimited;
use crate::path::PathKind;
use crate::INDENT;
@@ -36,7 +37,7 @@
self.word("[");
self.ty(&ty.elem);
self.word("; ");
- self.expr(&ty.len);
+ self.expr(&ty.len, FixupContext::NONE);
self.word("]");
}
diff --git a/crates/prettyplease/tests/test.rs b/crates/prettyplease/tests/test.rs
index 04004a7..aa6b849 100644
--- a/crates/prettyplease/tests/test.rs
+++ b/crates/prettyplease/tests/test.rs
@@ -20,7 +20,7 @@
},
indoc! {"
fn main() {
- if (Struct {} == Struct {}) {}
+ if (Struct {}) == (Struct {}) {}
}
"},
);
@@ -39,17 +39,11 @@
}
}
},
- // FIXME: no parens around `Struct {}` because anything until the `=>`
- // is considered part of the match guard expression. Parsing of the
- // expression is not terminated by `{` in that position.
- //
- // FIXME: the `true && false` needs parens. Otherwise the precedence is
- // `(let _ = true) && false` which means something different.
indoc! {"
fn main() {
match () {
- () if let _ = (Struct {}) => {}
- () if let _ = true && false => {}
+ () if let _ = Struct {} => {}
+ () if let _ = (true && false) => {}
}
}
"},
diff --git a/crates/prettyplease/tests/test_precedence.rs b/crates/prettyplease/tests/test_precedence.rs
new file mode 100644
index 0000000..f1eec23
--- /dev/null
+++ b/crates/prettyplease/tests/test_precedence.rs
@@ -0,0 +1,900 @@
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::ToTokens as _;
+use std::mem;
+use std::process::ExitCode;
+use syn::punctuated::Punctuated;
+use syn::visit_mut::{self, VisitMut};
+use syn::{
+ token, AngleBracketedGenericArguments, Arm, BinOp, Block, Expr, ExprArray, ExprAssign,
+ ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure,
+ ExprConst, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop,
+ ExprMacro, ExprMatch, ExprMethodCall, ExprPath, ExprRange, ExprRawAddr, ExprReference,
+ ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprUnary, ExprUnsafe, ExprWhile, ExprYield,
+ File, GenericArgument, Generics, Item, ItemConst, Label, Lifetime, LifetimeParam, Lit, LitInt,
+ Macro, MacroDelimiter, Member, Pat, PatWild, Path, PathArguments, PathSegment,
+ PointerMutability, QSelf, RangeLimits, ReturnType, Stmt, StmtMacro, Token, Type, TypeInfer,
+ TypeParam, TypePath, UnOp, Visibility,
+};
+
+struct FlattenParens;
+
+impl VisitMut for FlattenParens {
+ fn visit_expr_mut(&mut self, e: &mut Expr) {
+ while let Expr::Paren(paren) = e {
+ *e = mem::replace(&mut *paren.expr, Expr::PLACEHOLDER);
+ }
+ visit_mut::visit_expr_mut(self, e);
+ }
+}
+
+struct AsIfPrinted;
+
+impl VisitMut for AsIfPrinted {
+ fn visit_generics_mut(&mut self, generics: &mut Generics) {
+ if generics.params.is_empty() {
+ generics.lt_token = None;
+ generics.gt_token = None;
+ }
+ if let Some(where_clause) = &generics.where_clause {
+ if where_clause.predicates.is_empty() {
+ generics.where_clause = None;
+ }
+ }
+ visit_mut::visit_generics_mut(self, generics);
+ }
+
+ fn visit_lifetime_param_mut(&mut self, param: &mut LifetimeParam) {
+ if param.bounds.is_empty() {
+ param.colon_token = None;
+ }
+ visit_mut::visit_lifetime_param_mut(self, param);
+ }
+
+ fn visit_stmt_mut(&mut self, stmt: &mut Stmt) {
+ if let Stmt::Expr(expr, semi) = stmt {
+ if let Expr::Macro(e) = expr {
+ if match e.mac.delimiter {
+ MacroDelimiter::Brace(_) => true,
+ MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => semi.is_some(),
+ } {
+ let expr = match mem::replace(expr, Expr::PLACEHOLDER) {
+ Expr::Macro(expr) => expr,
+ _ => unreachable!(),
+ };
+ *stmt = Stmt::Macro(StmtMacro {
+ attrs: expr.attrs,
+ mac: expr.mac,
+ semi_token: *semi,
+ });
+ }
+ }
+ }
+ visit_mut::visit_stmt_mut(self, stmt);
+ }
+
+ fn visit_type_param_mut(&mut self, param: &mut TypeParam) {
+ if param.bounds.is_empty() {
+ param.colon_token = None;
+ }
+ visit_mut::visit_type_param_mut(self, param);
+ }
+}
+
+#[test]
+fn test_permutations() -> ExitCode {
+ fn iter(depth: usize, f: &mut dyn FnMut(Expr)) {
+ let span = Span::call_site();
+
+ // Expr::Path
+ f(Expr::Path(ExprPath {
+ // `x`
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("x", span)),
+ }));
+ if false {
+ f(Expr::Path(ExprPath {
+ // `x::<T>`
+ attrs: Vec::new(),
+ qself: None,
+ path: Path {
+ leading_colon: None,
+ segments: Punctuated::from_iter([PathSegment {
+ ident: Ident::new("x", span),
+ arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
+ colon2_token: Some(Token),
+ lt_token: Token,
+ args: Punctuated::from_iter([GenericArgument::Type(Type::Path(
+ TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ },
+ ))]),
+ gt_token: Token,
+ }),
+ }]),
+ },
+ }));
+ f(Expr::Path(ExprPath {
+ // `<T as Trait>::CONST`
+ attrs: Vec::new(),
+ qself: Some(QSelf {
+ lt_token: Token,
+ ty: Box::new(Type::Path(TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ })),
+ position: 1,
+ as_token: Some(Token),
+ gt_token: Token,
+ }),
+ path: Path {
+ leading_colon: None,
+ segments: Punctuated::from_iter([
+ PathSegment::from(Ident::new("Trait", span)),
+ PathSegment::from(Ident::new("CONST", span)),
+ ]),
+ },
+ }));
+ }
+
+ let depth = match depth.checked_sub(1) {
+ Some(depth) => depth,
+ None => return,
+ };
+
+ // Expr::Assign
+ iter(depth, &mut |expr| {
+ iter(0, &mut |simple| {
+ f(Expr::Assign(ExprAssign {
+ // `x = $expr`
+ attrs: Vec::new(),
+ left: Box::new(simple.clone()),
+ eq_token: Token,
+ right: Box::new(expr.clone()),
+ }));
+ f(Expr::Assign(ExprAssign {
+ // `$expr = x`
+ attrs: Vec::new(),
+ left: Box::new(expr.clone()),
+ eq_token: Token,
+ right: Box::new(simple),
+ }));
+ });
+ });
+
+ // Expr::Binary
+ iter(depth, &mut |expr| {
+ iter(0, &mut |simple| {
+ for op in [
+ BinOp::Add(Token),
+ //BinOp::Sub(Token),
+ //BinOp::Mul(Token),
+ //BinOp::Div(Token),
+ //BinOp::Rem(Token),
+ //BinOp::And(Token),
+ //BinOp::Or(Token),
+ //BinOp::BitXor(Token),
+ //BinOp::BitAnd(Token),
+ //BinOp::BitOr(Token),
+ //BinOp::Shl(Token),
+ //BinOp::Shr(Token),
+ //BinOp::Eq(Token),
+ BinOp::Lt(Token),
+ //BinOp::Le(Token),
+ //BinOp::Ne(Token),
+ //BinOp::Ge(Token),
+ //BinOp::Gt(Token),
+ BinOp::ShlAssign(Token),
+ ] {
+ f(Expr::Binary(ExprBinary {
+ // `x + $expr`
+ attrs: Vec::new(),
+ left: Box::new(simple.clone()),
+ op,
+ right: Box::new(expr.clone()),
+ }));
+ f(Expr::Binary(ExprBinary {
+ // `$expr + x`
+ attrs: Vec::new(),
+ left: Box::new(expr.clone()),
+ op,
+ right: Box::new(simple.clone()),
+ }));
+ }
+ });
+ });
+
+ // Expr::Block
+ f(Expr::Block(ExprBlock {
+ // `{}`
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Break
+ f(Expr::Break(ExprBreak {
+ // `break`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: None,
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Break(ExprBreak {
+ // `break $expr`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: None,
+ expr: Some(Box::new(expr)),
+ }));
+ });
+
+ // Expr::Call
+ iter(depth, &mut |expr| {
+ f(Expr::Call(ExprCall {
+ // `$expr()`
+ attrs: Vec::new(),
+ func: Box::new(expr),
+ paren_token: token::Paren(span),
+ args: Punctuated::new(),
+ }));
+ });
+
+ // Expr::Cast
+ iter(depth, &mut |expr| {
+ f(Expr::Cast(ExprCast {
+ // `$expr as T`
+ attrs: Vec::new(),
+ expr: Box::new(expr),
+ as_token: Token,
+ ty: Box::new(Type::Path(TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ })),
+ }));
+ });
+
+ // Expr::Closure
+ iter(depth, &mut |expr| {
+ f(Expr::Closure(ExprClosure {
+ // `|| $expr`
+ attrs: Vec::new(),
+ lifetimes: None,
+ constness: None,
+ movability: None,
+ asyncness: None,
+ capture: None,
+ or1_token: Token,
+ inputs: Punctuated::new(),
+ or2_token: Token,
+ output: ReturnType::Default,
+ body: Box::new(expr),
+ }));
+ });
+
+ // Expr::Field
+ iter(depth, &mut |expr| {
+ f(Expr::Field(ExprField {
+ // `$expr.field`
+ attrs: Vec::new(),
+ base: Box::new(expr),
+ dot_token: Token,
+ member: Member::Named(Ident::new("field", span)),
+ }));
+ });
+
+ // Expr::If
+ iter(depth, &mut |expr| {
+ f(Expr::If(ExprIf {
+ // `if $expr {}`
+ attrs: Vec::new(),
+ if_token: Token,
+ cond: Box::new(expr),
+ then_branch: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ else_branch: None,
+ }));
+ });
+
+ // Expr::Let
+ iter(depth, &mut |expr| {
+ f(Expr::Let(ExprLet {
+ attrs: Vec::new(),
+ let_token: Token,
+ pat: Box::new(Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ })),
+ eq_token: Token,
+ expr: Box::new(expr),
+ }));
+ });
+
+ // Expr::Range
+ f(Expr::Range(ExprRange {
+ // `..`
+ attrs: Vec::new(),
+ start: None,
+ limits: RangeLimits::HalfOpen(Token),
+ end: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Range(ExprRange {
+ // `..$expr`
+ attrs: Vec::new(),
+ start: None,
+ limits: RangeLimits::HalfOpen(Token),
+ end: Some(Box::new(expr.clone())),
+ }));
+ f(Expr::Range(ExprRange {
+ // `$expr..`
+ attrs: Vec::new(),
+ start: Some(Box::new(expr)),
+ limits: RangeLimits::HalfOpen(Token),
+ end: None,
+ }));
+ });
+
+ // Expr::Reference
+ iter(depth, &mut |expr| {
+ f(Expr::Reference(ExprReference {
+ // `&$expr`
+ attrs: Vec::new(),
+ and_token: Token,
+ mutability: None,
+ expr: Box::new(expr),
+ }));
+ });
+
+ // Expr::Return
+ f(Expr::Return(ExprReturn {
+ // `return`
+ attrs: Vec::new(),
+ return_token: Token,
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Return(ExprReturn {
+ // `return $expr`
+ attrs: Vec::new(),
+ return_token: Token,
+ expr: Some(Box::new(expr)),
+ }));
+ });
+
+ // Expr::Try
+ iter(depth, &mut |expr| {
+ f(Expr::Try(ExprTry {
+ // `$expr?`
+ attrs: Vec::new(),
+ expr: Box::new(expr),
+ question_token: Token,
+ }));
+ });
+
+ // Expr::Unary
+ iter(depth, &mut |expr| {
+ for op in [
+ UnOp::Deref(Token),
+ //UnOp::Not(Token),
+ //UnOp::Neg(Token),
+ ] {
+ f(Expr::Unary(ExprUnary {
+ // `*$expr`
+ attrs: Vec::new(),
+ op,
+ expr: Box::new(expr.clone()),
+ }));
+ }
+ });
+
+ if false {
+ // Expr::Array
+ f(Expr::Array(ExprArray {
+ // `[]`
+ attrs: Vec::new(),
+ bracket_token: token::Bracket(span),
+ elems: Punctuated::new(),
+ }));
+
+ // Expr::Async
+ f(Expr::Async(ExprAsync {
+ // `async {}`
+ attrs: Vec::new(),
+ async_token: Token,
+ capture: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Await
+ iter(depth, &mut |expr| {
+ f(Expr::Await(ExprAwait {
+ // `$expr.await`
+ attrs: Vec::new(),
+ base: Box::new(expr),
+ dot_token: Token,
+ await_token: Token,
+ }));
+ });
+
+ // Expr::Block
+ f(Expr::Block(ExprBlock {
+ // `'a: {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Block(ExprBlock {
+ // `{ $expr }`
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::from([Stmt::Expr(expr.clone(), None)]),
+ },
+ }));
+ f(Expr::Block(ExprBlock {
+ // `{ $expr; }`
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::from([Stmt::Expr(expr, Some(Token))]),
+ },
+ }));
+ });
+
+ // Expr::Break
+ f(Expr::Break(ExprBreak {
+ // `break 'a`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: Some(Lifetime::new("'a", span)),
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Break(ExprBreak {
+ // `break 'a $expr`
+ attrs: Vec::new(),
+ break_token: Token,
+ label: Some(Lifetime::new("'a", span)),
+ expr: Some(Box::new(expr)),
+ }));
+ });
+
+ // Expr::Closure
+ f(Expr::Closure(ExprClosure {
+ // `|| -> T {}`
+ attrs: Vec::new(),
+ lifetimes: None,
+ constness: None,
+ movability: None,
+ asyncness: None,
+ capture: None,
+ or1_token: Token,
+ inputs: Punctuated::new(),
+ or2_token: Token,
+ output: ReturnType::Type(
+ Token,
+ Box::new(Type::Path(TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ })),
+ ),
+ body: Box::new(Expr::Block(ExprBlock {
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ })),
+ }));
+
+ // Expr::Const
+ f(Expr::Const(ExprConst {
+ // `const {}`
+ attrs: Vec::new(),
+ const_token: Token,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Continue
+ f(Expr::Continue(ExprContinue {
+ // `continue`
+ attrs: Vec::new(),
+ continue_token: Token,
+ label: None,
+ }));
+ f(Expr::Continue(ExprContinue {
+ // `continue 'a`
+ attrs: Vec::new(),
+ continue_token: Token,
+ label: Some(Lifetime::new("'a", span)),
+ }));
+
+ // Expr::ForLoop
+ iter(depth, &mut |expr| {
+ f(Expr::ForLoop(ExprForLoop {
+ // `for _ in $expr {}`
+ attrs: Vec::new(),
+ label: None,
+ for_token: Token,
+ pat: Box::new(Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ })),
+ in_token: Token,
+ expr: Box::new(expr.clone()),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ f(Expr::ForLoop(ExprForLoop {
+ // `'a: for _ in $expr {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ for_token: Token,
+ pat: Box::new(Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ })),
+ in_token: Token,
+ expr: Box::new(expr),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ });
+
+ // Expr::Index
+ iter(depth, &mut |expr| {
+ f(Expr::Index(ExprIndex {
+ // `$expr[0]`
+ attrs: Vec::new(),
+ expr: Box::new(expr),
+ bracket_token: token::Bracket(span),
+ index: Box::new(Expr::Lit(ExprLit {
+ attrs: Vec::new(),
+ lit: Lit::Int(LitInt::new("0", span)),
+ })),
+ }));
+ });
+
+ // Expr::Loop
+ f(Expr::Loop(ExprLoop {
+ // `loop {}`
+ attrs: Vec::new(),
+ label: None,
+ loop_token: Token,
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ f(Expr::Loop(ExprLoop {
+ // `'a: loop {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ loop_token: Token,
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Macro
+ f(Expr::Macro(ExprMacro {
+ // `m!()`
+ attrs: Vec::new(),
+ mac: Macro {
+ path: Path::from(Ident::new("m", span)),
+ bang_token: Token,
+ delimiter: MacroDelimiter::Paren(token::Paren(span)),
+ tokens: TokenStream::new(),
+ },
+ }));
+ f(Expr::Macro(ExprMacro {
+ // `m! {}`
+ attrs: Vec::new(),
+ mac: Macro {
+ path: Path::from(Ident::new("m", span)),
+ bang_token: Token,
+ delimiter: MacroDelimiter::Brace(token::Brace(span)),
+ tokens: TokenStream::new(),
+ },
+ }));
+
+ // Expr::Match
+ iter(depth, &mut |expr| {
+ f(Expr::Match(ExprMatch {
+ // `match $expr {}`
+ attrs: Vec::new(),
+ match_token: Token,
+ expr: Box::new(expr.clone()),
+ brace_token: token::Brace(span),
+ arms: Vec::new(),
+ }));
+ f(Expr::Match(ExprMatch {
+ // `match x { _ => $expr }`
+ attrs: Vec::new(),
+ match_token: Token,
+ expr: Box::new(Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("x", span)),
+ })),
+ brace_token: token::Brace(span),
+ arms: Vec::from([Arm {
+ attrs: Vec::new(),
+ pat: Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ }),
+ guard: None,
+ fat_arrow_token: Token,
+ body: Box::new(expr.clone()),
+ comma: None,
+ }]),
+ }));
+ f(Expr::Match(ExprMatch {
+ // `match x { _ if $expr => {} }`
+ attrs: Vec::new(),
+ match_token: Token,
+ expr: Box::new(Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("x", span)),
+ })),
+ brace_token: token::Brace(span),
+ arms: Vec::from([Arm {
+ attrs: Vec::new(),
+ pat: Pat::Wild(PatWild {
+ attrs: Vec::new(),
+ underscore_token: Token,
+ }),
+ guard: Some((Token, Box::new(expr))),
+ fat_arrow_token: Token,
+ body: Box::new(Expr::Block(ExprBlock {
+ attrs: Vec::new(),
+ label: None,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ })),
+ comma: None,
+ }]),
+ }));
+ });
+
+ // Expr::MethodCall
+ iter(depth, &mut |expr| {
+ f(Expr::MethodCall(ExprMethodCall {
+ // `$expr.method()`
+ attrs: Vec::new(),
+ receiver: Box::new(expr.clone()),
+ dot_token: Token,
+ method: Ident::new("method", span),
+ turbofish: None,
+ paren_token: token::Paren(span),
+ args: Punctuated::new(),
+ }));
+ f(Expr::MethodCall(ExprMethodCall {
+ // `$expr.method::<T>()`
+ attrs: Vec::new(),
+ receiver: Box::new(expr),
+ dot_token: Token,
+ method: Ident::new("method", span),
+ turbofish: Some(AngleBracketedGenericArguments {
+ colon2_token: Some(Token),
+ lt_token: Token,
+ args: Punctuated::from_iter([GenericArgument::Type(Type::Path(
+ TypePath {
+ qself: None,
+ path: Path::from(Ident::new("T", span)),
+ },
+ ))]),
+ gt_token: Token,
+ }),
+ paren_token: token::Paren(span),
+ args: Punctuated::new(),
+ }));
+ });
+
+ // Expr::RawAddr
+ iter(depth, &mut |expr| {
+ f(Expr::RawAddr(ExprRawAddr {
+ // `&raw const $expr`
+ attrs: Vec::new(),
+ and_token: Token,
+ raw: Token,
+ mutability: PointerMutability::Const(Token),
+ expr: Box::new(expr),
+ }));
+ });
+
+ // Expr::Struct
+ f(Expr::Struct(ExprStruct {
+ // `Struct {}`
+ attrs: Vec::new(),
+ qself: None,
+ path: Path::from(Ident::new("Struct", span)),
+ brace_token: token::Brace(span),
+ fields: Punctuated::new(),
+ dot2_token: None,
+ rest: None,
+ }));
+
+ // Expr::TryBlock
+ f(Expr::TryBlock(ExprTryBlock {
+ // `try {}`
+ attrs: Vec::new(),
+ try_token: Token,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::Unsafe
+ f(Expr::Unsafe(ExprUnsafe {
+ // `unsafe {}`
+ attrs: Vec::new(),
+ unsafe_token: Token,
+ block: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+
+ // Expr::While
+ iter(depth, &mut |expr| {
+ f(Expr::While(ExprWhile {
+ // `while $expr {}`
+ attrs: Vec::new(),
+ label: None,
+ while_token: Token,
+ cond: Box::new(expr.clone()),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ f(Expr::While(ExprWhile {
+ // `'a: while $expr {}`
+ attrs: Vec::new(),
+ label: Some(Label {
+ name: Lifetime::new("'a", span),
+ colon_token: Token,
+ }),
+ while_token: Token,
+ cond: Box::new(expr),
+ body: Block {
+ brace_token: token::Brace(span),
+ stmts: Vec::new(),
+ },
+ }));
+ });
+
+ // Expr::Yield
+ f(Expr::Yield(ExprYield {
+ // `yield`
+ attrs: Vec::new(),
+ yield_token: Token,
+ expr: None,
+ }));
+ iter(depth, &mut |expr| {
+ f(Expr::Yield(ExprYield {
+ // `yield $expr`
+ attrs: Vec::new(),
+ yield_token: Token,
+ expr: Some(Box::new(expr)),
+ }));
+ });
+ }
+ }
+
+ let mut failures = 0;
+ macro_rules! fail {
+ ($($message:tt)*) => {{
+ eprintln!($($message)*);
+ failures += 1;
+ return;
+ }};
+ }
+ let mut assert = |mut original: Expr| {
+ let span = Span::call_site();
+ // `const _: () = $expr;`
+ let pretty = prettyplease::unparse(&File {
+ shebang: None,
+ attrs: Vec::new(),
+ items: Vec::from([Item::Const(ItemConst {
+ attrs: Vec::new(),
+ vis: Visibility::Inherited,
+ const_token: Token,
+ ident: Ident::from(Token),
+ generics: Generics::default(),
+ colon_token: Token,
+ ty: Box::new(Type::Infer(TypeInfer {
+ underscore_token: Token,
+ })),
+ eq_token: Token,
+ expr: Box::new(original.clone()),
+ semi_token: Token,
+ })]),
+ });
+ let mut parsed = match syn::parse_file(&pretty) {
+ Ok(parsed) => parsed,
+ _ => fail!("failed to parse: {pretty}{original:#?}"),
+ };
+ let item = match parsed.items.as_mut_slice() {
+ [Item::Const(item)] => item,
+ _ => unreachable!(),
+ };
+ let mut parsed = mem::replace(&mut *item.expr, Expr::PLACEHOLDER);
+ AsIfPrinted.visit_expr_mut(&mut original);
+ FlattenParens.visit_expr_mut(&mut parsed);
+ if original != parsed {
+ fail!(
+ "before: {}\n{:#?}\nafter: {}\n{:#?}",
+ original.to_token_stream(),
+ original,
+ parsed.to_token_stream(),
+ parsed,
+ );
+ }
+ if pretty.contains("(||") {
+ // https://github.com/dtolnay/prettyplease/issues/99
+ return;
+ }
+ let no_paren = pretty.replace(['(', ')'], "");
+ if pretty != no_paren {
+ if let Ok(mut parsed2) = syn::parse_file(&no_paren) {
+ let item = match parsed2.items.as_mut_slice() {
+ [Item::Const(item)] => item,
+ _ => unreachable!(),
+ };
+ if original == *item.expr {
+ fail!("redundant parens: {}", pretty);
+ }
+ }
+ }
+ };
+
+ iter(if cfg!(debug_assertions) { 3 } else { 4 }, &mut assert);
+ if failures > 0 {
+ eprintln!("FAILURES: {failures}");
+ ExitCode::FAILURE
+ } else {
+ ExitCode::SUCCESS
+ }
+}
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/tracing/.android-checksum.json b/crates/tracing/.android-checksum.json
index bbf0610..2e0a0d1 100644
--- a/crates/tracing/.android-checksum.json
+++ b/crates/tracing/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"f5dcc41dbbff7b7ee26a24c1cea3af9771aa19422f002b83fb4a75c1a143719d","Android.bp":"89c7bdc670500be6da0cdb45715b9cf25477c9145a612d1442b04ae0590f2486","CHANGELOG.md":"83d1f01bedb24102a630698ad8f37fd98622096411c31c2f8e9706e782da3d28","Cargo.toml":"6745f850b0f32e4484d3b37d4b0740c7cb841b42ecd5e35b394be8e8d06d3b3c","LICENSE":"81b5afff24bf73e7d6660ff9e03a493ba57f494dad837134a654b3945a3199d7","METADATA":"aa5d42841b8ef7e87b526623309bab410c964d5e17d1ba5061a86af968b49a1b","MODULE_LICENSE_MIT":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"2ea85e52967c6009ec9b14a38915c227bddbc4f20c451741d058c00a2472a346","benches/baseline.rs":"c7a2ecdb72fea8f9161ec12f3f8bb4a3108ab65dada9101431124cd383930e96","benches/dispatch_get_clone.rs":"7f9b901641eaf78c3b691036e84b25a6fdd2cfb4dbe0d6cf1939b3f872aad155","benches/dispatch_get_ref.rs":"d0a7adaf237e585a9e850f207d32bf358b91cf2702e9c17084d189e1ed4eda5c","benches/empty_span.rs":"eb214ec80a601dd7deec54109a9921bdc76e91d788412a8dd626cc55ba8a982d","benches/enter_span.rs":"424baadd508d8cbb3df4cd828f0d3b98e17cf29d897df42b6e30d71e3ab3239e","benches/event.rs":"f4e6a10225e162693cc30017bab847b647ce78bf403898bd2c1d9bcca48c3cb4","benches/shared.rs":"ffa86d1edea3b645a52eb8c519bb25c1868aae8da34c1c5ae88da559cdf94a57","benches/span_fields.rs":"3f8dabb263a914e00be25f907d6759b3a9f6516fb8a882893a3184df940e514b","benches/span_no_fields.rs":"d30b7664b9a9b100c77b68fb8584aca093fa4b41d69b53b37dccbea366e1c60a","benches/span_repeated.rs":"11db6c7f458e3e83e997fdcd22113d68d3016ba0c53e02b610600def434a3cc5","cargo_embargo.json":"862f3402e4541b7985c935104f2a607ee0fb5c0501c620611f8b47377a90b50d","src/dispatcher.rs":"6428903c6b66fe0f4f7105791f09d588c35cbe00ec864c0dd064c478eca900be","src/field.rs":"b7ea1a84b3fc4103a8f787bb400146ef71cab5f689e087ac961ab7dd1fada005","src/instrument.rs":"7f2528c2fcbeeb21c51a8af695583293e96af59a02a3c3b9406aa2f874b320e9","src/level_filters.rs":"e051513fc7f2a5a4e56c4fe86ceda80cb0395a5ccba6e49dc95b1aca2f612579","src/lib.rs":"8ecad32048ffe0f42853b6812dcdcd685a074db9e7ead55dbaa465d9b4b2ccaa","src/macros.rs":"c4784cd867c8e85c45a285dd00ad5fda5d4add11f8186692eae232133fabbf1b","src/span.rs":"28cb2843b76911e93017da4e969af8916602b5e450006154cd7c736295963a88","src/stdlib.rs":"db6b63e680c9285e1b30a5234c6b14a1e53f2ad4c41aa5fb582da33f07229af6","src/subscriber.rs":"a60aa85e9d41203ae2c68a1e11d5b3361f905ac1892ac56142159c3f87a02537","tests/enabled.rs":"087c65298c599c433db41c1e501d254d5a9c7e735f48a304131b1eadf80bbe4e","tests/event.rs":"22429e5a08cab80a82b4077438341ca333f5e296d665f221800513dcdb019cd4","tests/filter_caching_is_lexically_scoped.rs":"dc6b5cf37a3a9871a9744bb822087b5b94dd835c0472ef5f9b5a649b66779a4b","tests/filters_are_not_reevaluated_for_the_same_span.rs":"605efca02f1940b31927a4a388a21b76b5e834ac91ccf4943e5be17c9a170e7d","tests/filters_are_reevaluated_for_different_call_sites.rs":"5ff455da8ca7c08cb003bb24db43f781fd38010a45923aa9bdddb6f829beca72","tests/filters_dont_leak.rs":"09b12fa51c0d6f033d000b63215040b3e71c4351cc9ec08c81af0db414c106d5","tests/future_send.rs":"b88303ec21941a99db4ac7b8cfa1713c902df76ff3758ca3cc0a57807d76c3e5","tests/instrument.rs":"c0c70bb12415e49863c22ec1353ddd061cff5138c86978b99e8089d082588359","tests/macro_imports.rs":"21e2a293e400251273988178aad6ff0598fc5e7b5d76c8f0365777757438cc45","tests/macros.rs":"2a0f6868edaa84037fc0bba4ef7cb2d69abf9b477286068b4285a8b9ab6fee17","tests/macros_incompatible_concat.rs":"8ea190ebcc2b48ee079da79a4c9307925d161b4ae23cb49176de3da76dae80be","tests/max_level_hint.rs":"8e4a8c350e8a3bbbef459a6a4b79c09efa6f93064480830cf610ae8053fb749f","tests/multiple_max_level_hints.rs":"ed06f9924a612c72696319bb0845948c571760063e311008b4be559930169820","tests/no_subscriber.rs":"06ff5c207971a4b902489d87c76df6a9c353cc67760d47e8a459173ca3b9f96f","tests/register_callsite_deadlock.rs":"6e4915c321e9d50e54d556592e54c76d8d9b89e8e8f99f5b3856d30cadf60357","tests/scoped_clobbers_default.rs":"564b1ba437bdd22315d6597403540a884ef90e5227060f7925f50cd5e7b2b0f9","tests/span.rs":"97666a8561bb7fc0a02ab82db9b7481ee80f863b9f467a5e5f891b13137e8de0","tests/subscriber.rs":"95b0a92b1edf37e8c6af766bfc4833824df52f06d987ff3f0facefafa07dcb84"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"a903fa50d9ea75ee3f6cbb423c42244fca6f245c1c54907e4b4fca9f7a5a01ae","Android.bp":"139dd9b29e4b69e211f786e6733e66aa4a7045b530da1fd1567160ee2972b6ec","CHANGELOG.md":"3820ece64599fecff28ebf27bfd4544a160f20b292dd01746d8c10322eb15916","Cargo.toml":"0a98a9bce5b8d7b0159b9a46dfa400e8b2ab38f64fd1e0932ac3547f89293990","LICENSE":"81b5afff24bf73e7d6660ff9e03a493ba57f494dad837134a654b3945a3199d7","METADATA":"08635ba5c4060353da6c5d872356b506d1991f28a04aea7da9ab21622735d897","MODULE_LICENSE_MIT":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"56830f1adde979de325afc5c6e63eb8587820e5d9914c978923d250bad2c74f3","benches/baseline.rs":"c7a2ecdb72fea8f9161ec12f3f8bb4a3108ab65dada9101431124cd383930e96","benches/dispatch_get_clone.rs":"7f9b901641eaf78c3b691036e84b25a6fdd2cfb4dbe0d6cf1939b3f872aad155","benches/dispatch_get_ref.rs":"d0a7adaf237e585a9e850f207d32bf358b91cf2702e9c17084d189e1ed4eda5c","benches/empty_span.rs":"eb214ec80a601dd7deec54109a9921bdc76e91d788412a8dd626cc55ba8a982d","benches/enter_span.rs":"424baadd508d8cbb3df4cd828f0d3b98e17cf29d897df42b6e30d71e3ab3239e","benches/event.rs":"f4e6a10225e162693cc30017bab847b647ce78bf403898bd2c1d9bcca48c3cb4","benches/shared.rs":"ffa86d1edea3b645a52eb8c519bb25c1868aae8da34c1c5ae88da559cdf94a57","benches/span_fields.rs":"3f8dabb263a914e00be25f907d6759b3a9f6516fb8a882893a3184df940e514b","benches/span_no_fields.rs":"d30b7664b9a9b100c77b68fb8584aca093fa4b41d69b53b37dccbea366e1c60a","benches/span_repeated.rs":"11db6c7f458e3e83e997fdcd22113d68d3016ba0c53e02b610600def434a3cc5","cargo_embargo.json":"862f3402e4541b7985c935104f2a607ee0fb5c0501c620611f8b47377a90b50d","src/dispatcher.rs":"6428903c6b66fe0f4f7105791f09d588c35cbe00ec864c0dd064c478eca900be","src/field.rs":"b7ea1a84b3fc4103a8f787bb400146ef71cab5f689e087ac961ab7dd1fada005","src/instrument.rs":"7f2528c2fcbeeb21c51a8af695583293e96af59a02a3c3b9406aa2f874b320e9","src/level_filters.rs":"e051513fc7f2a5a4e56c4fe86ceda80cb0395a5ccba6e49dc95b1aca2f612579","src/lib.rs":"9238b7ec3e6da493e23713ed2d2bfdd02debe22f35253a43115aa6912b96a54d","src/macros.rs":"95caf438e46177f16ec14c862aecac58067e2d486a6fd4ebaea96f3be251805d","src/span.rs":"2cecd85243746c1157055ca72759dd27918be9a708c3db15288ff21c2077e581","src/stdlib.rs":"db6b63e680c9285e1b30a5234c6b14a1e53f2ad4c41aa5fb582da33f07229af6","src/subscriber.rs":"a60aa85e9d41203ae2c68a1e11d5b3361f905ac1892ac56142159c3f87a02537","tests/enabled.rs":"68fd024e00f9e352ae6fd2e1a1b78326aa9497d354ed39da07cfb1fb762fc4ff","tests/event.rs":"082473fa3cf9985044cb051618ffa2dc2ee69a0d0423b7ff7d5ab35999efb18e","tests/filter_caching_is_lexically_scoped.rs":"dc6b5cf37a3a9871a9744bb822087b5b94dd835c0472ef5f9b5a649b66779a4b","tests/filters_are_not_reevaluated_for_the_same_span.rs":"605efca02f1940b31927a4a388a21b76b5e834ac91ccf4943e5be17c9a170e7d","tests/filters_are_reevaluated_for_different_call_sites.rs":"5ff455da8ca7c08cb003bb24db43f781fd38010a45923aa9bdddb6f829beca72","tests/filters_dont_leak.rs":"09b12fa51c0d6f033d000b63215040b3e71c4351cc9ec08c81af0db414c106d5","tests/future_send.rs":"b88303ec21941a99db4ac7b8cfa1713c902df76ff3758ca3cc0a57807d76c3e5","tests/instrument.rs":"e03a0219fdab3e8d079217131d46dcc9dacb63f8a8d80a3f1ffc3bf86d32460c","tests/macro_imports.rs":"21e2a293e400251273988178aad6ff0598fc5e7b5d76c8f0365777757438cc45","tests/macros.rs":"2ab4495fab861baad4aa18879ccc38d0c6be22d52aa20a62be6787fe4630d22a","tests/macros_incompatible_concat.rs":"8ea190ebcc2b48ee079da79a4c9307925d161b4ae23cb49176de3da76dae80be","tests/max_level_hint.rs":"8e4a8c350e8a3bbbef459a6a4b79c09efa6f93064480830cf610ae8053fb749f","tests/missed_register_callsite.rs":"ce18308700d928cf0db5572c6456fa240e48ac8692c300d1ce6af89ab1fd398f","tests/multiple_max_level_hints.rs":"ed06f9924a612c72696319bb0845948c571760063e311008b4be559930169820","tests/no_subscriber.rs":"06ff5c207971a4b902489d87c76df6a9c353cc67760d47e8a459173ca3b9f96f","tests/register_callsite_deadlock.rs":"6e4915c321e9d50e54d556592e54c76d8d9b89e8e8f99f5b3856d30cadf60357","tests/scoped_clobbers_default.rs":"d2facc4119bcfc11c3f2d190263f377058ce2888e02456c1c1a6a4df1eb20b4d","tests/span.rs":"dedcc71df91790f3835945d3a051f021db11e2d4382a0c1491be909116e9aec9","tests/subscriber.rs":"be0f5ae5bc923a62c73eb01908ff9aa614ff98fa5f5e770f349bd2fc44a18893"}}
\ No newline at end of file
diff --git a/crates/tracing/.cargo-checksum.json b/crates/tracing/.cargo-checksum.json
index 92c7ecf..a22e4ee 100644
--- a/crates/tracing/.cargo-checksum.json
+++ b/crates/tracing/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"CHANGELOG.md":"8f2a5e3f70b90ef91e724b0089f511f6ce95cc6afaf1da30d3d486e30935c520","Cargo.toml":"e8262bfd368f8b9c2f902db368a71da88f446584167392a2611b8eb08e411175","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"baed1881843d68618241ccc57e2120993f6eb67c0e1987115b6855f7d328def3","benches/baseline.rs":"43a3e31b6c33dba2e6328052301b707b212487b83f0dcffc843061a9c48a2319","benches/dispatch_get_clone.rs":"866239abeb74a82440741c948a4e7e0a44e92e8cc87319ec57e3b057c9e8f5dd","benches/dispatch_get_ref.rs":"dd2803259a6784c256304e676bbce05de233e4c8451ac85863787213343e9be7","benches/empty_span.rs":"9f51cf376414ea751b2f50c357f2435a545d606118286f5b8b89f185e28aad8c","benches/enter_span.rs":"4410ec73d277e7b54e9f306c00ff3b79a150d1832966b7fc29984c8e3ad8d57c","benches/event.rs":"98de3c82ed18abe0a3cbe6eda9a4f9deec2b69bca42c3aac11dea4b608b85a67","benches/shared.rs":"2623311af7d153685064e664a5903d03e7dc3179754c324f3a76f29f060515e6","benches/span_fields.rs":"9166cd43ef2783e5419dd61ea57a02e48e8cc38aa1b357e9b79fa581929b60d8","benches/span_no_fields.rs":"79cc4befacf27d7ce728246087c4f06a6066f913e831d9043caeb7941f0193f6","benches/span_repeated.rs":"e4b3c99a7a9fc15d9042b8db399a56cf647b4eebd26f29d95325bb057b68330b","src/dispatcher.rs":"a8732392ffe56b1178f8fd3d6e6e02d40b51475c38bb4600abd9cd170df1bf6c","src/field.rs":"fe6c2eb36f92880254a707a465f873ca84510d93f06614b9b36ba20b48bf209d","src/instrument.rs":"034b1c7e46ab2fae331ea215a3d1b1a2211ef6b18d7e7113523e3ef5bf3ca9bb","src/level_filters.rs":"92aca5797a59b2f3c34adfb896260400c2602456eec0fe9c7d49204df37ff699","src/lib.rs":"54a1168ed65d746ce1cc6668a78931131af2afc7e0530391dcffcaa58a9f7971","src/macros.rs":"6ffcbea4b4b402b46e9e074f0808fb755b59ce2bb1c97aa4acdfdb31bb26bf4c","src/span.rs":"4efa37a8fc7604fcc3547eac488122c13bcb28e136f46381082480e0036217a0","src/stdlib.rs":"248514a9bae6106e436358aee44c92abf8e7f79022895c4a25136ddef211d198","src/subscriber.rs":"8933d8766439f929c0a98a0863d20aff37b221314b3825edd9058be511149968","tests/enabled.rs":"a80fd3c70be439b0d1d2509b46a2b9ca31748aab4341f67d0fa721f32c6a65a1","tests/event.rs":"d3c6d688fc1938516770a9fb486252bbe8e95f24b37097a2291e7119b6e78373","tests/filter_caching_is_lexically_scoped.rs":"5487a37db5fbdf3d57020ab1f01185d928c45d967d99d723ffc434540459d8dc","tests/filters_are_not_reevaluated_for_the_same_span.rs":"251abbc000dddd298448958a1f0e5be71da527ac6c1a368d57837c83a5467329","tests/filters_are_reevaluated_for_different_call_sites.rs":"e0fdd8e930c043674702831b4d96f331e63aba824576bbac50b3f53bb0241cc7","tests/filters_dont_leak.rs":"6c41d3209cf2a37a1656a8314399777022ceec556e44f5417f1f5a4cd24eb43a","tests/future_send.rs":"3e9c9193219d12e342c18bbedb2f6ec940334202feb3cffba91601d6001b8575","tests/instrument.rs":"e46cf30db3ff0174e4e0885720b97531c56cbe7d05bb4580e38790f272856b56","tests/macro_imports.rs":"d5de857162185d4a2384f3cb644bfcf76c7f5c1a3b5f72bfa0d2620ac6e3873c","tests/macros.rs":"acb6987a37be2035cde28c8a6b3cecbdbffffafebe6c007712cd145afbd1beb8","tests/macros_incompatible_concat.rs":"5f3bcbb65e4ae39db1cfc2def62fc913c20bab0fb769c8f731504e2615585ee5","tests/max_level_hint.rs":"39576a958df3ea215c49154ff72c3dd14c8b3be3fcdb849e7dd847c8c9fd09e2","tests/multiple_max_level_hints.rs":"28ead97a8424358a0632547dae987ddbddc5bb69cdcd996d20feb49a7f3ddc69","tests/no_subscriber.rs":"ca42d0d4a20eb3b4e0a342b5e0090c7ada8b5a157f3cddb7b5d051238cdc1202","tests/register_callsite_deadlock.rs":"c0b3142543e7a10065c7583a8ee0b6bc978ea4f3979599651101c5a28966e7c8","tests/scoped_clobbers_default.rs":"5fd48ff207df64b0e2ee443de75da0d8bd3b0817c6d9b003b0b00157ec244d7b","tests/span.rs":"2a5a8c666c8a2b51a1a1222e6046a1067690f039d15ef8b56d0e83bffa76f78d","tests/subscriber.rs":"ed3c02e8a75c6e4fc187a024fde6319e6d1ea95c993eda3fd9d559fd41fe7cce"},"package":"c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"}
\ No newline at end of file
+{"files":{"CHANGELOG.md":"e41336be068976091df56bff671d38042286b776bbeb087f4bf6f4e85631cb81","Cargo.toml":"6c23bca19dcda69692ad102f2c395d5f098635cdcd27091c467837251827522f","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"0293821b9246c4b1a74211b74f2ce9655951931b8411eb365dced29a4ef46af5","benches/baseline.rs":"43a3e31b6c33dba2e6328052301b707b212487b83f0dcffc843061a9c48a2319","benches/dispatch_get_clone.rs":"866239abeb74a82440741c948a4e7e0a44e92e8cc87319ec57e3b057c9e8f5dd","benches/dispatch_get_ref.rs":"dd2803259a6784c256304e676bbce05de233e4c8451ac85863787213343e9be7","benches/empty_span.rs":"9f51cf376414ea751b2f50c357f2435a545d606118286f5b8b89f185e28aad8c","benches/enter_span.rs":"4410ec73d277e7b54e9f306c00ff3b79a150d1832966b7fc29984c8e3ad8d57c","benches/event.rs":"98de3c82ed18abe0a3cbe6eda9a4f9deec2b69bca42c3aac11dea4b608b85a67","benches/shared.rs":"2623311af7d153685064e664a5903d03e7dc3179754c324f3a76f29f060515e6","benches/span_fields.rs":"9166cd43ef2783e5419dd61ea57a02e48e8cc38aa1b357e9b79fa581929b60d8","benches/span_no_fields.rs":"79cc4befacf27d7ce728246087c4f06a6066f913e831d9043caeb7941f0193f6","benches/span_repeated.rs":"e4b3c99a7a9fc15d9042b8db399a56cf647b4eebd26f29d95325bb057b68330b","src/dispatcher.rs":"a8732392ffe56b1178f8fd3d6e6e02d40b51475c38bb4600abd9cd170df1bf6c","src/field.rs":"fe6c2eb36f92880254a707a465f873ca84510d93f06614b9b36ba20b48bf209d","src/instrument.rs":"034b1c7e46ab2fae331ea215a3d1b1a2211ef6b18d7e7113523e3ef5bf3ca9bb","src/level_filters.rs":"92aca5797a59b2f3c34adfb896260400c2602456eec0fe9c7d49204df37ff699","src/lib.rs":"7a934a9dcec4f847c2b1534f49417dba2068050af017a857e12b660ac06f9a6f","src/macros.rs":"ba1df10032f8832536dfdd43e567dc0e260d8679fea30ccf68061bc58f9e1cce","src/span.rs":"8c5cce0ab990960c5e7bc0dd99dd9eefde6d8d2477e98c39f17dffa9170c4185","src/stdlib.rs":"248514a9bae6106e436358aee44c92abf8e7f79022895c4a25136ddef211d198","src/subscriber.rs":"8933d8766439f929c0a98a0863d20aff37b221314b3825edd9058be511149968","tests/enabled.rs":"e19d039cf0e984918838aaab9d8b3ca5a1eea9d01a54a264a70184b440a9e880","tests/event.rs":"f51b286c1224346f96f38bee6bed9c4eac5e6405f269e90b770ebfb370820fa1","tests/filter_caching_is_lexically_scoped.rs":"5487a37db5fbdf3d57020ab1f01185d928c45d967d99d723ffc434540459d8dc","tests/filters_are_not_reevaluated_for_the_same_span.rs":"251abbc000dddd298448958a1f0e5be71da527ac6c1a368d57837c83a5467329","tests/filters_are_reevaluated_for_different_call_sites.rs":"e0fdd8e930c043674702831b4d96f331e63aba824576bbac50b3f53bb0241cc7","tests/filters_dont_leak.rs":"6c41d3209cf2a37a1656a8314399777022ceec556e44f5417f1f5a4cd24eb43a","tests/future_send.rs":"3e9c9193219d12e342c18bbedb2f6ec940334202feb3cffba91601d6001b8575","tests/instrument.rs":"a3ee726247d897ad2af8123e6d44abce1a43a72ccbc3fd479257f810d8cbbbb2","tests/macro_imports.rs":"d5de857162185d4a2384f3cb644bfcf76c7f5c1a3b5f72bfa0d2620ac6e3873c","tests/macros.rs":"71474339414ff573471de125f2d369da8caffbfe47fa2e9a18f2e87de20d2a79","tests/macros_incompatible_concat.rs":"5f3bcbb65e4ae39db1cfc2def62fc913c20bab0fb769c8f731504e2615585ee5","tests/max_level_hint.rs":"39576a958df3ea215c49154ff72c3dd14c8b3be3fcdb849e7dd847c8c9fd09e2","tests/missed_register_callsite.rs":"3211f0f030c9baf6333abf709d6520ced25619ee82cdf88a4e6ba8e1aff12296","tests/multiple_max_level_hints.rs":"28ead97a8424358a0632547dae987ddbddc5bb69cdcd996d20feb49a7f3ddc69","tests/no_subscriber.rs":"ca42d0d4a20eb3b4e0a342b5e0090c7ada8b5a157f3cddb7b5d051238cdc1202","tests/register_callsite_deadlock.rs":"c0b3142543e7a10065c7583a8ee0b6bc978ea4f3979599651101c5a28966e7c8","tests/scoped_clobbers_default.rs":"a7615615d176fb2a4b877c48d2aa7a75fca436d997d9672b56cc2c30177f889b","tests/span.rs":"91995aa0f59b09a6230ec146abcafce5710e6657159e4b6f47a56fb6ec76be8c","tests/subscriber.rs":"2da999bedb978bab019a70ccc9fdd43b062ec2e383af1e7c5b780df23adc219b"},"package":"784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"}
\ No newline at end of file
diff --git a/crates/tracing/Android.bp b/crates/tracing/Android.bp
index 0e003d3..145d826 100644
--- a/crates/tracing/Android.bp
+++ b/crates/tracing/Android.bp
@@ -18,7 +18,7 @@
host_supported: true,
crate_name: "tracing",
cargo_env_compat: true,
- cargo_pkg_version: "0.1.40",
+ cargo_pkg_version: "0.1.41",
crate_root: "src/lib.rs",
edition: "2018",
features: [
@@ -46,7 +46,7 @@
host_supported: true,
crate_name: "tracing",
cargo_env_compat: true,
- cargo_pkg_version: "0.1.40",
+ cargo_pkg_version: "0.1.41",
crate_root: "src/lib.rs",
edition: "2018",
features: [
diff --git a/crates/tracing/CHANGELOG.md b/crates/tracing/CHANGELOG.md
index db1668b..0c9f5f4 100644
--- a/crates/tracing/CHANGELOG.md
+++ b/crates/tracing/CHANGELOG.md
@@ -1,4 +1,57 @@
-# 0.1.40
+# 0.1.41 (November 27, 2024)
+
+[ [crates.io][crate-0.1.41] ] | [ [docs.rs][docs-0.1.41] ]
+
+This release updates the `tracing-core` dependency to [v0.1.33][core-0.1.33] and
+the `tracing-attributes` dependency to [v0.1.28][attrs-0.1.28].
+
+### Added
+
+- **core**: Add index API for `Field` ([#2820])
+- **core**: Allow `&[u8]` to be recorded as event/span field ([#2954])
+
+### Changed
+
+- Bump MSRV to 1.63 ([#2793])
+- **core**: Use const `thread_local`s when possible ([#2838])
+
+### Fixed
+
+- Removed core imports in macros ([#2762])
+- **attributes**: Added missing RecordTypes for instrument ([#2781])
+- **attributes**: Change order of async and unsafe modifier ([#2864])
+- Fix missing field prefixes ([#2878])
+- **attributes**: Extract match scrutinee ([#2880])
+- Fix non-simple macro usage without message ([#2879])
+- Fix event macros with constant field names in the first position ([#2883])
+- Allow field path segments to be keywords ([#2925])
+- **core**: Fix missed `register_callsite` error ([#2938])
+- **attributes**: Support const values for `target` and `name` ([#2941])
+- Prefix macro calls with ::core to avoid clashing with local macros ([#3024])
+
+[#2762]: https://github.com/tokio-rs/tracing/pull/2762
+[#2781]: https://github.com/tokio-rs/tracing/pull/2781
+[#2793]: https://github.com/tokio-rs/tracing/pull/2793
+[#2820]: https://github.com/tokio-rs/tracing/pull/2820
+[#2838]: https://github.com/tokio-rs/tracing/pull/2838
+[#2864]: https://github.com/tokio-rs/tracing/pull/2864
+[#2878]: https://github.com/tokio-rs/tracing/pull/2878
+[#2879]: https://github.com/tokio-rs/tracing/pull/2879
+[#2880]: https://github.com/tokio-rs/tracing/pull/2880
+[#2883]: https://github.com/tokio-rs/tracing/pull/2883
+[#2925]: https://github.com/tokio-rs/tracing/pull/2925
+[#2938]: https://github.com/tokio-rs/tracing/pull/2938
+[#2941]: https://github.com/tokio-rs/tracing/pull/2941
+[#2954]: https://github.com/tokio-rs/tracing/pull/2954
+[#3024]: https://github.com/tokio-rs/tracing/pull/3024
+[attrs-0.1.28]:
+ https://github.com/tokio-rs/tracing/releases/tag/tracing-attributes-0.1.28
+[core-0.1.33]:
+ https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.33
+[docs-0.1.41]: https://docs.rs/tracing/0.1.41/tracing/
+[crate-0.1.41]: https://crates.io/crates/tracing/0.1.41
+
+# 0.1.40 (October 19, 2023)
This release fixes a potential stack use-after-free in the
`Instrument::into_inner` method. Only uses of this method are affected by this
@@ -651,7 +704,7 @@
filtering, improving performance when a span or event is disabled by a
`static_max_level_XXX` feature flag (#868)
- `LevelFilter` is now a re-export of the `tracing_core::LevelFilter` type, it
- can now be used interchangably with the versions in `tracing-core` and
+ can now be used interchangeably with the versions in `tracing-core` and
`tracing-subscriber` (#853)
- Significant performance improvements when comparing `LevelFilter`s and
`Level`s (#853)
diff --git a/crates/tracing/Cargo.toml b/crates/tracing/Cargo.toml
index 1143cd6..e71eb5f 100644
--- a/crates/tracing/Cargo.toml
+++ b/crates/tracing/Cargo.toml
@@ -11,13 +11,18 @@
[package]
edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
authors = [
"Eliza Weisman <[email protected]>",
"Tokio Contributors <[email protected]>",
]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
description = """
Application-level tracing for Rust.
"""
@@ -37,6 +42,7 @@
]
license = "MIT"
repository = "https://github.com/tokio-rs/tracing"
+resolver = "2"
[package.metadata.docs.rs]
all-features = true
@@ -51,40 +57,133 @@
"tracing_unstable",
]
+[lib]
+name = "tracing"
+path = "src/lib.rs"
+
+[[test]]
+name = "enabled"
+path = "tests/enabled.rs"
+
+[[test]]
+name = "event"
+path = "tests/event.rs"
+
+[[test]]
+name = "filter_caching_is_lexically_scoped"
+path = "tests/filter_caching_is_lexically_scoped.rs"
+
+[[test]]
+name = "filters_are_not_reevaluated_for_the_same_span"
+path = "tests/filters_are_not_reevaluated_for_the_same_span.rs"
+
+[[test]]
+name = "filters_are_reevaluated_for_different_call_sites"
+path = "tests/filters_are_reevaluated_for_different_call_sites.rs"
+
+[[test]]
+name = "filters_dont_leak"
+path = "tests/filters_dont_leak.rs"
+
+[[test]]
+name = "future_send"
+path = "tests/future_send.rs"
+
+[[test]]
+name = "instrument"
+path = "tests/instrument.rs"
+
+[[test]]
+name = "macro_imports"
+path = "tests/macro_imports.rs"
+
+[[test]]
+name = "macros"
+path = "tests/macros.rs"
+
+[[test]]
+name = "macros_incompatible_concat"
+path = "tests/macros_incompatible_concat.rs"
+
+[[test]]
+name = "max_level_hint"
+path = "tests/max_level_hint.rs"
+
+[[test]]
+name = "missed_register_callsite"
+path = "tests/missed_register_callsite.rs"
+
+[[test]]
+name = "multiple_max_level_hints"
+path = "tests/multiple_max_level_hints.rs"
+
+[[test]]
+name = "no_subscriber"
+path = "tests/no_subscriber.rs"
+
+[[test]]
+name = "register_callsite_deadlock"
+path = "tests/register_callsite_deadlock.rs"
+
+[[test]]
+name = "scoped_clobbers_default"
+path = "tests/scoped_clobbers_default.rs"
+
+[[test]]
+name = "span"
+path = "tests/span.rs"
+
+[[test]]
+name = "subscriber"
+path = "tests/subscriber.rs"
+
[[bench]]
name = "baseline"
+path = "benches/baseline.rs"
harness = false
[[bench]]
name = "dispatch_get_clone"
+path = "benches/dispatch_get_clone.rs"
harness = false
[[bench]]
name = "dispatch_get_ref"
+path = "benches/dispatch_get_ref.rs"
harness = false
[[bench]]
name = "empty_span"
+path = "benches/empty_span.rs"
harness = false
[[bench]]
name = "enter_span"
+path = "benches/enter_span.rs"
harness = false
[[bench]]
name = "event"
+path = "benches/event.rs"
harness = false
[[bench]]
+name = "shared"
+path = "benches/shared.rs"
+
+[[bench]]
name = "span_fields"
+path = "benches/span_fields.rs"
harness = false
[[bench]]
name = "span_no_fields"
+path = "benches/span_no_fields.rs"
harness = false
[[bench]]
name = "span_repeated"
+path = "benches/span_repeated.rs"
harness = false
[dependencies.log]
@@ -95,20 +194,20 @@
version = "0.2.9"
[dependencies.tracing-attributes]
-version = "0.1.27"
+version = "0.1.28"
optional = true
[dependencies.tracing-core]
-version = "0.1.32"
+version = "0.1.33"
default-features = false
[dev-dependencies.criterion]
version = "0.3.6"
-default_features = false
+default-features = false
[dev-dependencies.futures]
version = "0.3.21"
-default_features = false
+default-features = false
[dev-dependencies.log]
version = "0.4.17"
@@ -136,8 +235,16 @@
std = ["tracing-core/std"]
valuable = ["tracing-core/valuable"]
-[target."cfg(target_arch = \"wasm32\")".dev-dependencies.wasm-bindgen-test]
-version = "^0.3"
+[target.'cfg(target_arch = "wasm32")'.dev-dependencies.wasm-bindgen-test]
+version = "0.3.38"
[badges.maintenance]
status = "actively-developed"
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+priority = 0
+check-cfg = [
+ "cfg(flaky_tests)",
+ "cfg(tracing_unstable)",
+]
diff --git a/crates/tracing/METADATA b/crates/tracing/METADATA
index 8990ac4..3cc6f0c 100644
--- a/crates/tracing/METADATA
+++ b/crates/tracing/METADATA
@@ -1,17 +1,17 @@
name: "tracing"
description: "A scoped, structured logging and diagnostics system."
third_party {
- version: "0.1.40"
+ version: "0.1.41"
license_type: NOTICE
last_upgrade_date {
- year: 2024
- month: 2
- day: 6
+ year: 2025
+ month: 1
+ day: 15
}
homepage: "https://crates.io/crates/tracing"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/tracing/tracing-0.1.40.crate"
- version: "0.1.40"
+ value: "https://static.crates.io/crates/tracing/tracing-0.1.41.crate"
+ version: "0.1.41"
}
}
diff --git a/crates/tracing/README.md b/crates/tracing/README.md
index a42636f..0bd4208 100644
--- a/crates/tracing/README.md
+++ b/crates/tracing/README.md
@@ -47,7 +47,7 @@
The `tracing` crate provides the APIs necessary for instrumenting libraries
and applications to emit trace data.
-*Compiler support: [requires `rustc` 1.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
[msrv]: #supported-rust-versions
@@ -145,7 +145,7 @@
// the `#[tracing::instrument]` attribute creates and enters a span
// every time the instrumented function is called. The span is named after the
-// the function or method. Paramaters passed to the function are recorded as fields.
+// the function or method. Parameters passed to the function are recorded as fields.
#[tracing::instrument]
pub fn shave(yak: usize) -> Result<(), Box<dyn Error + 'static>> {
// this creates an event at the DEBUG level with two fields:
@@ -185,7 +185,7 @@
if let Err(ref error) = res {
// Like spans, events can also use the field initialization shorthand.
- // In this instance, `yak` is the field being initalized.
+ // In this instance, `yak` is the field being initialized.
error!(yak, error = error.as_ref(), "failed to shave yak!");
} else {
yaks_shaved += 1;
@@ -250,7 +250,7 @@
is as long as the future's.
The second, and preferred, option is through the
-[`#[instrument]`](https://docs.rs/tracing/0.1.38/tracing/attr.instrument.html)
+[`#[instrument]`](https://docs.rs/tracing/0.1.41/tracing/attr.instrument.html)
attribute:
```rust
@@ -297,7 +297,7 @@
// Dropping the span will close it, indicating that it has ended.
```
-The [`#[instrument]`](https://docs.rs/tracing/0.1.38/tracing/attr.instrument.html) attribute macro
+The [`#[instrument]`](https://docs.rs/tracing/0.1.41/tracing/attr.instrument.html) attribute macro
can reduce some of this boilerplate:
```rust
@@ -397,6 +397,7 @@
- [`sentry-tracing`] provides a layer for reporting events and traces to [Sentry].
- [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
- [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
+- [`json-subscriber`] provides a layer for emitting JSON logs. The output can be customized much more than with [`FmtSubscriber`]'s JSON output.
If you're the maintainer of a `tracing` ecosystem crate not listed above,
please let us know! We'd love to add your project to the list!
@@ -428,6 +429,7 @@
[`tracing-loki`]: https://crates.io/crates/tracing-loki
[Grafana Loki]: https://grafana.com/oss/loki/
[`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
+[`json-subscriber`]: https://crates.io/crates/json-subscriber
**Note:** that some of the ecosystem crates are currently unreleased and
undergoing active development. They may be less stable than `tracing` and
@@ -445,7 +447,7 @@
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs
index 258cbe5..8b6c7f0 100644
--- a/crates/tracing/src/lib.rs
+++ b/crates/tracing/src/lib.rs
@@ -19,7 +19,7 @@
//! The `tracing` crate provides the APIs necessary for instrumenting libraries
//! and applications to emit trace data.
//!
-//! *Compiler support: [requires `rustc` 1.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//! # Core Concepts
@@ -173,7 +173,7 @@
//! For functions which don't have built-in tracing support and can't have
//! the `#[instrument]` attribute applied (such as from an external crate),
//! the [`Span` struct][`Span`] has a [`in_scope()` method][`in_scope`]
-//! which can be used to easily wrap synchonous code in a span.
+//! which can be used to easily wrap synchronous code in a span.
//!
//! For example:
//! ```rust
@@ -756,6 +756,7 @@
//! - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests.
//! - [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs.
//! - [`clippy-tracing`] provides a tool to add, remove and check for `tracing::instrument`.
+//! - [`json-subscriber`] provides a subscriber for emitting JSON logs. The output can be customized much more than with [`tracing-subscriber`]'s JSON output.
//!
//! If you're the maintainer of a `tracing` ecosystem crate not listed above,
//! please let us know! We'd love to add your project to the list!
@@ -799,6 +800,7 @@
//! [`reqwest`]: https://crates.io/crates/reqwest
//! [`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch
//! [`clippy-tracing`]: https://crates.io/crates/clippy-tracing
+//! [`json-subscriber`]: https://crates.io/crates/json-subscriber
//!
//! <pre class="ignore" style="white-space:normal;font:inherit;">
//! <strong>Note</strong>: Some of these ecosystem crates are currently
@@ -871,7 +873,7 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
@@ -929,7 +931,8 @@
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
- private_in_public,
+ private_interfaces,
+ private_bounds,
unconditional_recursion,
unused,
unused_allocation,
@@ -984,7 +987,10 @@
pub mod __macro_support {
pub use crate::callsite::Callsite;
use crate::{subscriber::Interest, Metadata};
- pub use core::concat;
+ // Re-export the `core` functions that are used in macros. This allows
+ // a crate to be named `core` and avoid name clashes.
+ // See here: https://github.com/tokio-rs/tracing/issues/2761
+ pub use core::{concat, file, format_args, iter::Iterator, line, option::Option};
/// Callsite implementation used by macro-generated code.
///
diff --git a/crates/tracing/src/macros.rs b/crates/tracing/src/macros.rs
index dba49d1..156334a 100644
--- a/crates/tracing/src/macros.rs
+++ b/crates/tracing/src/macros.rs
@@ -627,7 +627,7 @@
target: $target,
parent: $parent,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -678,7 +678,7 @@
name: $name,
target: $target,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -694,9 +694,9 @@
static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
name: $crate::__macro_support::concat!(
"event ",
- file!(),
+ $crate::__macro_support::file!(),
":",
- line!()
+ $crate::__macro_support::line!()
),
kind: $crate::metadata::Kind::EVENT,
target: $target,
@@ -736,7 +736,7 @@
target: $target,
parent: $parent,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -789,7 +789,7 @@
name: $name,
parent: $parent,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -839,7 +839,7 @@
$crate::event!(
name: $name,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -855,9 +855,9 @@
static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
name: $crate::__macro_support::concat!(
"event ",
- file!(),
+ $crate::__macro_support::file!(),
":",
- line!()
+ $crate::__macro_support::line!()
),
kind: $crate::metadata::Kind::EVENT,
target: $target,
@@ -894,7 +894,7 @@
$crate::event!(
target: $target,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -910,7 +910,7 @@
target: module_path!(),
parent: $parent,
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
(parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => (
@@ -970,7 +970,7 @@
$crate::event!(
target: module_path!(),
$lvl,
- { message = ::core::format_args!($($arg)+), $($fields)* }
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
)
);
( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
@@ -1117,15 +1117,15 @@
/// in false positives or false negatives include:
///
/// - If a subscriber is using a filter which may enable a span or event based
-/// on field names, but `enabled!` is invoked without listing field names,
-/// `enabled!` may return a false negative if a specific field name would
-/// cause the subscriber to enable something that would otherwise be disabled.
+/// on field names, but `enabled!` is invoked without listing field names,
+/// `enabled!` may return a false negative if a specific field name would
+/// cause the subscriber to enable something that would otherwise be disabled.
/// - If a subscriber is using a filter which enables or disables specific events by
-/// file path and line number, a particular event may be enabled/disabled
-/// even if an `enabled!` invocation with the same level, target, and fields
-/// indicated otherwise.
+/// file path and line number, a particular event may be enabled/disabled
+/// even if an `enabled!` invocation with the same level, target, and fields
+/// indicated otherwise.
/// - The subscriber can choose to enable _only_ spans or _only_ events, which `enabled`
-/// will not reflect.
+/// will not reflect.
///
/// `enabled!()` requires a [level](crate::Level) argument, an optional `target:`
/// argument, and an optional set of field names. If the fields are not provided,
@@ -1188,9 +1188,9 @@
static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
name: $crate::__macro_support::concat!(
"enabled ",
- file!(),
+ $crate::__macro_support::file!(),
":",
- line!()
+ $crate::__macro_support::line!()
),
kind: $kind.hint(),
target: $target,
@@ -1305,14 +1305,14 @@
(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1322,14 +1322,14 @@
(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
@@ -1339,14 +1339,14 @@
(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
);
- (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
);
(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1356,14 +1356,14 @@
(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
);
- (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
);
(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1547,7 +1547,6 @@
$crate::event!(
target: module_path!(),
$crate::Level::TRACE,
- {},
$($arg)+
)
);
@@ -1582,14 +1581,14 @@
(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1599,14 +1598,14 @@
(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1616,14 +1615,14 @@
(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
);
- (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
);
(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1633,14 +1632,14 @@
(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
);
- (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
);
(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1824,7 +1823,6 @@
$crate::event!(
target: module_path!(),
$crate::Level::DEBUG,
- {},
$($arg)+
)
);
@@ -1870,14 +1868,14 @@
(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1887,14 +1885,14 @@
(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
@@ -1904,14 +1902,14 @@
(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
);
- (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
);
(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1921,14 +1919,14 @@
(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
);
- (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
);
(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -2112,7 +2110,6 @@
$crate::event!(
target: module_path!(),
$crate::Level::INFO,
- {},
$($arg)+
)
);
@@ -2151,14 +2148,14 @@
(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2168,14 +2165,14 @@
(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
@@ -2185,14 +2182,14 @@
(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
);
- (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
);
(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2202,14 +2199,14 @@
(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
);
- (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
);
(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2393,7 +2390,6 @@
$crate::event!(
target: module_path!(),
$crate::Level::WARN,
- {},
$($arg)+
)
);
@@ -2428,14 +2424,14 @@
(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2445,14 +2441,14 @@
(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
);
- (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
);
- (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
);
(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
@@ -2462,14 +2458,14 @@
(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
);
- (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
);
- (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
);
(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2479,14 +2475,14 @@
(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
);
- (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
);
- (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
- $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+ (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
);
(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
$crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2670,7 +2666,6 @@
$crate::event!(
target: module_path!(),
$crate::Level::ERROR,
- {},
$($arg)+
)
);
@@ -2802,79 +2797,79 @@
// };
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&$($k).+ as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$($k).+) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&$($k).+ as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$($k).+) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
$next,
)
};
@@ -2882,40 +2877,40 @@
// Handle literal names
(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
$next,
$($rest)*
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
$next,
)
};
(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => {
$crate::valueset!(
- @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
$next,
)
};
@@ -2963,7 +2958,7 @@
// Remainder is unparsable, but exists --- must be format args!
(@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
- $crate::valueset!(@ { (&$next, ::core::option::Option::Some(&::core::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
+ $crate::valueset!(@ { (&$next, $crate::__macro_support::Option::Some(&$crate::__macro_support::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
};
// === entry ===
@@ -2974,7 +2969,7 @@
let mut iter = $fields.iter();
$fields.value_set($crate::valueset!(
@ { },
- ::core::iter::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
+ $crate::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
$($kvs)+
))
}
@@ -3041,7 +3036,7 @@
$crate::fieldset!(@ { $($out),*, $k } $($rest)*)
};
- // Remainder is unparseable, but exists --- must be format args!
+ // Remainder is unparsable, but exists --- must be format args!
(@ { $(,)* $($out:expr),* } $($rest:tt)+) => {
$crate::fieldset!(@ { "message", $($out),*, })
};
@@ -3071,8 +3066,8 @@
#[doc(hidden)]
#[macro_export]
macro_rules! __tracing_stringify {
- ($s:expr) => {
- stringify!($s)
+ ($($t:tt)*) => {
+ stringify!($($t)*)
};
}
diff --git a/crates/tracing/src/span.rs b/crates/tracing/src/span.rs
index 3c235dc..8085391 100644
--- a/crates/tracing/src/span.rs
+++ b/crates/tracing/src/span.rs
@@ -1104,20 +1104,14 @@
/// Returns a [`Field`][super::field::Field] for the field with the
/// given `name`, if one exists,
- pub fn field<Q: ?Sized>(&self, field: &Q) -> Option<field::Field>
- where
- Q: field::AsField,
- {
+ pub fn field<Q: field::AsField + ?Sized>(&self, field: &Q) -> Option<field::Field> {
self.metadata().and_then(|meta| field.as_field(meta))
}
/// Returns true if this `Span` has a field for the given
/// [`Field`][super::field::Field] or field name.
#[inline]
- pub fn has_field<Q: ?Sized>(&self, field: &Q) -> bool
- where
- Q: field::AsField,
- {
+ pub fn has_field<Q: field::AsField + ?Sized>(&self, field: &Q) -> bool {
self.field(field).is_some()
}
@@ -1191,11 +1185,11 @@
///
/// [`field::Empty`]: super::field::Empty
/// [`Metadata`]: super::Metadata
- pub fn record<Q: ?Sized, V>(&self, field: &Q, value: V) -> &Self
- where
- Q: field::AsField,
- V: field::Value,
- {
+ pub fn record<Q: field::AsField + ?Sized, V: field::Value>(
+ &self,
+ field: &Q,
+ value: V,
+ ) -> &Self {
if let Some(meta) = self.meta {
if let Some(field) = field.as_field(meta) {
self.record_all(
@@ -1607,14 +1601,6 @@
mod test {
use super::*;
- trait AssertSend: Send {}
- impl AssertSend for Span {}
-
- trait AssertSync: Sync {}
- impl AssertSync for Span {}
- impl AssertSync for Entered<'_> {}
- impl AssertSync for EnteredSpan {}
-
#[test]
fn test_record_backwards_compat() {
Span::current().record("some-key", "some text");
diff --git a/crates/tracing/tests/enabled.rs b/crates/tracing/tests/enabled.rs
index 5a45963..e00b319 100644
--- a/crates/tracing/tests/enabled.rs
+++ b/crates/tracing/tests/enabled.rs
@@ -43,7 +43,7 @@
let _guard = tracing::subscriber::set_default(subscriber);
- // Ensure that the `_event` and `_span` alternatives work corretly
+ // Ensure that the `_event` and `_span` alternatives work correctly
assert!(!tracing::event_enabled!(Level::TRACE));
assert!(tracing::event_enabled!(Level::DEBUG));
assert!(tracing::span_enabled!(Level::TRACE));
diff --git a/crates/tracing/tests/event.rs b/crates/tracing/tests/event.rs
index 0be7c0b..25b5bbe 100644
--- a/crates/tracing/tests/event.rs
+++ b/crates/tracing/tests/event.rs
@@ -86,7 +86,7 @@
.and(
expect::field("question").with_value(&"life, the universe, and everything"),
)
- .and(field::msg(format_args!(
+ .and(expect::msg(format_args!(
"hello from my event! tricky? {:?}!",
true
)))
@@ -115,7 +115,7 @@
.and(
expect::field("question").with_value(&"life, the universe, and everything"),
)
- .and(field::msg(format_args!("hello from my event")))
+ .and(expect::msg(format_args!("hello from my event")))
.only(),
),
)
@@ -338,7 +338,7 @@
fn explicit_child() {
let (subscriber, handle) = subscriber::mock()
.new_span(expect::span().named("foo"))
- .event(expect::event().with_explicit_parent(Some("foo")))
+ .event(expect::event().with_ancestry(expect::has_explicit_parent("foo")))
.only()
.run_with_handle();
@@ -355,11 +355,11 @@
fn explicit_child_at_levels() {
let (subscriber, handle) = subscriber::mock()
.new_span(expect::span().named("foo"))
- .event(expect::event().with_explicit_parent(Some("foo")))
- .event(expect::event().with_explicit_parent(Some("foo")))
- .event(expect::event().with_explicit_parent(Some("foo")))
- .event(expect::event().with_explicit_parent(Some("foo")))
- .event(expect::event().with_explicit_parent(Some("foo")))
+ .event(expect::event().with_ancestry(expect::has_explicit_parent("foo")))
+ .event(expect::event().with_ancestry(expect::has_explicit_parent("foo")))
+ .event(expect::event().with_ancestry(expect::has_explicit_parent("foo")))
+ .event(expect::event().with_ancestry(expect::has_explicit_parent("foo")))
+ .event(expect::event().with_ancestry(expect::has_explicit_parent("foo")))
.only()
.run_with_handle();
@@ -527,6 +527,12 @@
let (subscriber, handle) = subscriber::mock()
.event(expect_event())
.event(expect_event())
+ .event(expect_event())
+ .event(expect_event())
+ .event(expect_event())
+ .event(expect_event())
+ .event(expect_event())
+ .event(expect_event())
.only()
.run_with_handle();
@@ -548,7 +554,67 @@
},
"quux"
);
+ tracing::info!(
+ { std::convert::identity(FOO) } = "bar",
+ { "constant string" } = "also works",
+ foo.bar = "baz",
+ "quux"
+ );
+ tracing::info!(
+ {
+ { std::convert::identity(FOO) } = "bar",
+ { "constant string" } = "also works",
+ foo.bar = "baz",
+ },
+ "quux"
+ );
+ tracing::event!(
+ Level::INFO,
+ { std::convert::identity(FOO) } = "bar",
+ { "constant string" } = "also works",
+ foo.bar = "baz",
+ "{}",
+ "quux"
+ );
+ tracing::event!(
+ Level::INFO,
+ {
+ { std::convert::identity(FOO) } = "bar",
+ { "constant string" } = "also works",
+ foo.bar = "baz",
+ },
+ "{}",
+ "quux"
+ );
+ tracing::info!(
+ { std::convert::identity(FOO) } = "bar",
+ { "constant string" } = "also works",
+ foo.bar = "baz",
+ "{}",
+ "quux"
+ );
+ tracing::info!(
+ {
+ { std::convert::identity(FOO) } = "bar",
+ { "constant string" } = "also works",
+ foo.bar = "baz",
+ },
+ "{}",
+ "quux"
+ );
});
handle.assert_finished();
}
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn keyword_ident_in_field_name() {
+ let (subscriber, handle) = subscriber::mock()
+ .event(expect::event().with_fields(expect::field("crate").with_value(&"tracing")))
+ .only()
+ .run_with_handle();
+
+ with_default(subscriber, || error!(crate = "tracing", "message"));
+ handle.assert_finished();
+}
diff --git a/crates/tracing/tests/instrument.rs b/crates/tracing/tests/instrument.rs
index 5247249..eb7e9ed 100644
--- a/crates/tracing/tests/instrument.rs
+++ b/crates/tracing/tests/instrument.rs
@@ -21,6 +21,7 @@
}
}
+ #[allow(dead_code)] // Field not used, but logs on `Drop`
struct Fut(Option<AssertSpanOnDrop>);
impl Future for Fut {
@@ -34,13 +35,21 @@
let subscriber = subscriber::mock()
.enter(expect::span().named("foo"))
- .event(expect::event().at_level(Level::INFO))
+ .event(
+ expect::event()
+ .with_ancestry(expect::has_contextual_parent("foo"))
+ .at_level(Level::INFO),
+ )
.exit(expect::span().named("foo"))
.enter(expect::span().named("foo"))
.exit(expect::span().named("foo"))
.drop_span(expect::span().named("foo"))
.enter(expect::span().named("bar"))
- .event(expect::event().at_level(Level::INFO))
+ .event(
+ expect::event()
+ .with_ancestry(expect::has_contextual_parent("bar"))
+ .at_level(Level::INFO),
+ )
.exit(expect::span().named("bar"))
.drop_span(expect::span().named("bar"))
.only()
diff --git a/crates/tracing/tests/macros.rs b/crates/tracing/tests/macros.rs
index 81b929d..a072389 100644
--- a/crates/tracing/tests/macros.rs
+++ b/crates/tracing/tests/macros.rs
@@ -5,15 +5,21 @@
#[cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
-// TODO: remove this once https://github.com/tokio-rs/tracing/pull/2675#issuecomment-1667628907 is resolved
-#[cfg(target_arch = "wasm32")]
-use ::core::option::Option::None;
-
use tracing::{
callsite, debug, debug_span, enabled, error, error_span, event, event_enabled, info, info_span,
span, span_enabled, trace, trace_span, warn, warn_span, Level,
};
+/// A type that implements `Display` and `Debug`, but not `Value`.
+#[derive(Debug)]
+struct DisplayDebug;
+
+impl ::std::fmt::Display for DisplayDebug {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ ::std::write!(f, "Foo")
+ }
+}
+
// Tests that macros work across various invocation syntax.
//
// These are quite repetitive, and _could_ be generated by a macro. However,
@@ -549,12 +555,15 @@
trace!(foo = ?3, bar.baz = %2, quux = false);
trace!(foo = 3, bar.baz = 2, quux = false);
trace!(foo = 3, bar.baz = 3,);
+ trace!("foo" = 3, bar.baz = 3,);
+ trace!(foo = 3, "bar.baz" = 3,);
trace!("foo");
trace!("foo: {}", 3);
trace!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
trace!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
trace!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
trace!({ foo = 3, bar.baz = 80 }, "quux");
+ trace!({ "foo" = 3, "bar.baz" = 80 }, "quux");
trace!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
trace!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
trace!({ foo = 2, bar.baz = 78 }, "quux");
@@ -573,6 +582,9 @@
trace!(?foo);
trace!(%foo);
trace!(foo);
+ trace!("foo" = ?foo);
+ trace!("foo" = %foo);
+ trace!("foo" = foo);
trace!(name: "foo", ?foo);
trace!(name: "foo", %foo);
trace!(name: "foo", foo);
@@ -585,6 +597,22 @@
trace!(target: "foo_events", ?foo, true, "message");
trace!(target: "foo_events", %foo, true, "message");
trace!(target: "foo_events", foo, true, "message");
+ trace!(name: "foo", target: "foo_events", ?foo);
+ trace!(name: "foo", target: "foo_events", %foo);
+ trace!(name: "foo", target: "foo_events", foo);
+ let foo = DisplayDebug;
+ trace!(?foo);
+ trace!(%foo);
+ trace!(name: "foo", ?foo);
+ trace!(name: "foo", %foo);
+ trace!(name: "foo", ?foo, true, "message");
+ trace!(name: "foo", %foo, true, "message");
+ trace!(target: "foo_events", ?foo);
+ trace!(target: "foo_events", %foo);
+ trace!(target: "foo_events", ?foo, true, "message");
+ trace!(target: "foo_events", %foo, true, "message");
+ trace!(name: "foo", target: "foo_events", ?foo, true, "message");
+ trace!(name: "foo", target: "foo_events", %foo, true, "message");
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -593,12 +621,15 @@
debug!(foo = ?3, bar.baz = %2, quux = false);
debug!(foo = 3, bar.baz = 2, quux = false);
debug!(foo = 3, bar.baz = 3,);
+ debug!("foo" = 3, bar.baz = 3,);
+ debug!(foo = 3, "bar.baz" = 3,);
debug!("foo");
debug!("foo: {}", 3);
debug!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
debug!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
debug!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
debug!({ foo = 3, bar.baz = 80 }, "quux");
+ debug!({ "foo" = 3, "bar.baz" = 80 }, "quux");
debug!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
debug!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
debug!({ foo = 2, bar.baz = 78 }, "quux");
@@ -617,6 +648,9 @@
debug!(?foo);
debug!(%foo);
debug!(foo);
+ debug!("foo" = ?foo);
+ debug!("foo" = %foo);
+ debug!("foo" = foo);
debug!(name: "foo", ?foo);
debug!(name: "foo", %foo);
debug!(name: "foo", foo);
@@ -629,6 +663,22 @@
debug!(target: "foo_events", ?foo, true, "message");
debug!(target: "foo_events", %foo, true, "message");
debug!(target: "foo_events", foo, true, "message");
+ debug!(name: "foo", target: "foo_events", ?foo);
+ debug!(name: "foo", target: "foo_events", %foo);
+ debug!(name: "foo", target: "foo_events", foo);
+ let foo = DisplayDebug;
+ debug!(?foo);
+ debug!(%foo);
+ debug!(name: "foo", ?foo);
+ debug!(name: "foo", %foo);
+ debug!(name: "foo", ?foo, true, "message");
+ debug!(name: "foo", %foo, true, "message");
+ debug!(target: "foo_events", ?foo);
+ debug!(target: "foo_events", %foo);
+ debug!(target: "foo_events", ?foo, true, "message");
+ debug!(target: "foo_events", %foo, true, "message");
+ debug!(name: "foo", target: "foo_events", ?foo, true, "message");
+ debug!(name: "foo", target: "foo_events", %foo, true, "message");
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -637,12 +687,15 @@
info!(foo = ?3, bar.baz = %2, quux = false);
info!(foo = 3, bar.baz = 2, quux = false);
info!(foo = 3, bar.baz = 3,);
+ info!("foo" = 3, bar.baz = 3,);
+ info!(foo = 3, "bar.baz" = 3,);
info!("foo");
info!("foo: {}", 3);
info!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
info!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
info!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
info!({ foo = 3, bar.baz = 80 }, "quux");
+ info!({ "foo" = 3, "bar.baz" = 80 }, "quux");
info!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
info!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
info!({ foo = 2, bar.baz = 78 }, "quux");
@@ -661,6 +714,9 @@
info!(?foo);
info!(%foo);
info!(foo);
+ info!("foo" = ?foo);
+ info!("foo" = %foo);
+ info!("foo" = foo);
info!(name: "foo", ?foo);
info!(name: "foo", %foo);
info!(name: "foo", foo);
@@ -673,6 +729,22 @@
info!(target: "foo_events", ?foo, true, "message");
info!(target: "foo_events", %foo, true, "message");
info!(target: "foo_events", foo, true, "message");
+ info!(name: "foo", target: "foo_events", ?foo);
+ info!(name: "foo", target: "foo_events", %foo);
+ info!(name: "foo", target: "foo_events", foo);
+ let foo = DisplayDebug;
+ info!(?foo);
+ info!(%foo);
+ info!(name: "foo", ?foo);
+ info!(name: "foo", %foo);
+ info!(name: "foo", ?foo, true, "message");
+ info!(name: "foo", %foo, true, "message");
+ info!(target: "foo_events", ?foo);
+ info!(target: "foo_events", %foo);
+ info!(target: "foo_events", ?foo, true, "message");
+ info!(target: "foo_events", %foo, true, "message");
+ info!(name: "foo", target: "foo_events", ?foo, true, "message");
+ info!(name: "foo", target: "foo_events", %foo, true, "message");
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -681,12 +753,15 @@
warn!(foo = ?3, bar.baz = %2, quux = false);
warn!(foo = 3, bar.baz = 2, quux = false);
warn!(foo = 3, bar.baz = 3,);
+ warn!("foo" = 3, bar.baz = 3,);
+ warn!(foo = 3, "bar.baz" = 3,);
warn!("foo");
warn!("foo: {}", 3);
warn!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
warn!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
warn!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
warn!({ foo = 3, bar.baz = 80 }, "quux");
+ warn!({ "foo" = 3, "bar.baz" = 80 }, "quux");
warn!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
warn!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
warn!({ foo = 2, bar.baz = 78 }, "quux");
@@ -705,6 +780,9 @@
warn!(?foo);
warn!(%foo);
warn!(foo);
+ warn!("foo" = ?foo);
+ warn!("foo" = %foo);
+ warn!("foo" = foo);
warn!(name: "foo", ?foo);
warn!(name: "foo", %foo);
warn!(name: "foo", foo);
@@ -717,6 +795,22 @@
warn!(target: "foo_events", ?foo, true, "message");
warn!(target: "foo_events", %foo, true, "message");
warn!(target: "foo_events", foo, true, "message");
+ warn!(name: "foo", target: "foo_events", ?foo);
+ warn!(name: "foo", target: "foo_events", %foo);
+ warn!(name: "foo", target: "foo_events", foo);
+ let foo = DisplayDebug;
+ warn!(?foo);
+ warn!(%foo);
+ warn!(name: "foo", ?foo);
+ warn!(name: "foo", %foo);
+ warn!(name: "foo", ?foo, true, "message");
+ warn!(name: "foo", %foo, true, "message");
+ warn!(target: "foo_events", ?foo);
+ warn!(target: "foo_events", %foo);
+ warn!(target: "foo_events", ?foo, true, "message");
+ warn!(target: "foo_events", %foo, true, "message");
+ warn!(name: "foo", target: "foo_events", ?foo, true, "message");
+ warn!(name: "foo", target: "foo_events", %foo, true, "message");
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -725,15 +819,18 @@
error!(foo = ?3, bar.baz = %2, quux = false);
error!(foo = 3, bar.baz = 2, quux = false);
error!(foo = 3, bar.baz = 3,);
+ error!("foo" = 3, bar.baz = 3,);
+ error!(foo = 3, "bar.baz" = 3,);
error!("foo");
error!("foo: {}", 3);
error!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
error!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
error!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
error!({ foo = 3, bar.baz = 80 }, "quux");
+ error!({ "foo" = 3, "bar.baz" = 80 }, "quux");
error!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
error!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
- error!({ foo = 2, bar.baz = 78, }, "quux");
+ error!({ foo = 2, bar.baz = 78 }, "quux");
error!({ foo = ?2, bar.baz = %78 }, "quux");
error!(name: "foo", foo = 3, bar.baz = 2, quux = false);
error!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
@@ -749,6 +846,9 @@
error!(?foo);
error!(%foo);
error!(foo);
+ error!("foo" = ?foo);
+ error!("foo" = %foo);
+ error!("foo" = foo);
error!(name: "foo", ?foo);
error!(name: "foo", %foo);
error!(name: "foo", foo);
@@ -761,6 +861,22 @@
error!(target: "foo_events", ?foo, true, "message");
error!(target: "foo_events", %foo, true, "message");
error!(target: "foo_events", foo, true, "message");
+ error!(name: "foo", target: "foo_events", ?foo);
+ error!(name: "foo", target: "foo_events", %foo);
+ error!(name: "foo", target: "foo_events", foo);
+ let foo = DisplayDebug;
+ error!(?foo);
+ error!(%foo);
+ error!(name: "foo", ?foo);
+ error!(name: "foo", %foo);
+ error!(name: "foo", ?foo, true, "message");
+ error!(name: "foo", %foo, true, "message");
+ error!(target: "foo_events", ?foo);
+ error!(target: "foo_events", %foo);
+ error!(target: "foo_events", ?foo, true, "message");
+ error!(target: "foo_events", %foo, true, "message");
+ error!(name: "foo", target: "foo_events", ?foo, true, "message");
+ error!(name: "foo", target: "foo_events", %foo, true, "message");
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
diff --git a/crates/tracing/tests/missed_register_callsite.rs b/crates/tracing/tests/missed_register_callsite.rs
new file mode 100644
index 0000000..d14db12
--- /dev/null
+++ b/crates/tracing/tests/missed_register_callsite.rs
@@ -0,0 +1,109 @@
+use std::{
+ ptr,
+ sync::atomic::{AtomicPtr, Ordering},
+ thread::{self, JoinHandle},
+ time::Duration,
+};
+
+use tracing::Subscriber;
+use tracing_core::{span, Metadata};
+
+struct TestSubscriber {
+ creator_thread: String,
+ sleep: Duration,
+ callsite: AtomicPtr<Metadata<'static>>,
+}
+
+impl TestSubscriber {
+ fn new(sleep_micros: u64) -> Self {
+ let creator_thread = thread::current()
+ .name()
+ .unwrap_or("<unknown thread>")
+ .to_owned();
+ Self {
+ creator_thread,
+ sleep: Duration::from_micros(sleep_micros),
+ callsite: AtomicPtr::new(ptr::null_mut()),
+ }
+ }
+}
+
+impl Subscriber for TestSubscriber {
+ fn register_callsite(&self, metadata: &'static Metadata<'static>) -> tracing_core::Interest {
+ if !self.sleep.is_zero() {
+ thread::sleep(self.sleep);
+ }
+
+ self.callsite
+ .store(metadata as *const _ as *mut _, Ordering::SeqCst);
+ println!(
+ "{creator} from {thread:?}: register_callsite: {callsite:#?}",
+ creator = self.creator_thread,
+ callsite = metadata as *const _,
+ thread = thread::current().name(),
+ );
+ tracing_core::Interest::always()
+ }
+
+ fn event(&self, event: &tracing_core::Event<'_>) {
+ let stored_callsite = self.callsite.load(Ordering::SeqCst);
+ let event_callsite: *mut Metadata<'static> = event.metadata() as *const _ as *mut _;
+
+ println!(
+ "{creator} from {thread:?}: event (with callsite): {event_callsite:#?} (stored callsite: {stored_callsite:#?})",
+ creator = self.creator_thread,
+ thread = thread::current().name(),
+ );
+
+ // This assert is the actual test.
+ assert_eq!(
+ stored_callsite, event_callsite,
+ "stored callsite: {stored_callsite:#?} does not match event \
+ callsite: {event_callsite:#?}. Was `event` called before \
+ `register_callsite`?"
+ );
+ }
+
+ fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
+ true
+ }
+ fn new_span(&self, _span: &span::Attributes<'_>) -> span::Id {
+ span::Id::from_u64(0)
+ }
+ fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {}
+ fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {}
+ fn enter(&self, _span: &tracing_core::span::Id) {}
+ fn exit(&self, _span: &tracing_core::span::Id) {}
+}
+
+fn subscriber_thread(idx: usize, register_sleep_micros: u64) -> JoinHandle<()> {
+ thread::Builder::new()
+ .name(format!("subscriber-{idx}"))
+ .spawn(move || {
+ // We use a sleep to ensure the starting order of the 2 threads.
+ let subscriber = TestSubscriber::new(register_sleep_micros);
+ let _subscriber_guard = tracing::subscriber::set_default(subscriber);
+
+ tracing::info!("event-from-{idx}", idx = idx);
+
+ // Wait a bit for everything to end (we don't want to remove the subscriber
+ // immediately because that will mix up the test).
+ thread::sleep(Duration::from_millis(100));
+ })
+ .expect("failed to spawn thread")
+}
+
+#[test]
+fn event_before_register() {
+ let subscriber_1_register_sleep_micros = 100;
+ let subscriber_2_register_sleep_micros = 0;
+
+ let jh1 = subscriber_thread(1, subscriber_1_register_sleep_micros);
+
+ // This delay ensures that the event!() in the first thread is executed first.
+ thread::sleep(Duration::from_micros(50));
+ let jh2 = subscriber_thread(2, subscriber_2_register_sleep_micros);
+
+ jh1.join().expect("failed to join thread");
+ jh2.join().expect("failed to join thread");
+}
diff --git a/crates/tracing/tests/scoped_clobbers_default.rs b/crates/tracing/tests/scoped_clobbers_default.rs
index dfd6fc9..41bb138 100644
--- a/crates/tracing/tests/scoped_clobbers_default.rs
+++ b/crates/tracing/tests/scoped_clobbers_default.rs
@@ -1,18 +1,18 @@
#![cfg(feature = "std")]
-use tracing_mock::*;
+use tracing_mock::{expect, subscriber};
#[test]
fn scoped_clobbers_global() {
// Reproduces https://github.com/tokio-rs/tracing/issues/2050
let (scoped, scoped_handle) = subscriber::mock()
- .event(event::msg("before global"))
- .event(event::msg("before drop"))
+ .event(expect::event().with_fields(expect::msg("before global")))
+ .event(expect::event().with_fields(expect::msg("before drop")))
.only()
.run_with_handle();
let (global, global_handle) = subscriber::mock()
- .event(event::msg("after drop"))
+ .event(expect::event().with_fields(expect::msg("after drop")))
.only()
.run_with_handle();
diff --git a/crates/tracing/tests/span.rs b/crates/tracing/tests/span.rs
index 09f1be8..9f4e5c4 100644
--- a/crates/tracing/tests/span.rs
+++ b/crates/tracing/tests/span.rs
@@ -6,6 +6,7 @@
use std::thread;
use tracing::{
+ error_span,
field::{debug, display},
subscriber::with_default,
Level, Span,
@@ -342,7 +343,7 @@
fn moved_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
@@ -373,7 +374,7 @@
.new_span(
expect::span()
.named("foo")
- .with_field(expect::field("fields.bar").with_value(&true).only()),
+ .with_fields(expect::field("fields.bar").with_value(&true).only()),
)
.only()
.run_with_handle();
@@ -389,7 +390,7 @@
fn borrowed_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
@@ -432,7 +433,7 @@
};
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("x")
.with_value(&debug(3.234))
.and(expect::field("y").with_value(&debug(-1.223)))
@@ -442,7 +443,7 @@
.new_span(
expect::span()
.named("bar")
- .with_field(expect::field("position").with_value(&debug(&pos)).only()),
+ .with_fields(expect::field("position").with_value(&debug(&pos)).only()),
)
.run_with_handle();
@@ -465,7 +466,7 @@
fn float_values() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("x")
.with_value(&3.234)
.and(expect::field("y").with_value(&-1.223))
@@ -492,7 +493,7 @@
.new_span(
expect::span()
.named("foo")
- .with_field(expect::field("bar").with_value(&5)
+ .with_fields(expect::field("bar").with_value(&5)
.and(expect::field("baz").with_value).only()),
)
.record(
@@ -549,7 +550,7 @@
fn record_new_value_for_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&5)
.and(expect::field("baz").with_value(&false))
@@ -580,7 +581,7 @@
fn record_new_values_for_fields() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&4)
.and(expect::field("baz").with_value(&false))
@@ -635,7 +636,11 @@
#[test]
fn explicit_root_span_is_root() {
let (subscriber, handle) = subscriber::mock()
- .new_span(expect::span().named("foo").with_explicit_parent(None))
+ .new_span(
+ expect::span()
+ .named("foo")
+ .with_ancestry(expect::is_explicit_root()),
+ )
.only()
.run_with_handle();
@@ -652,7 +657,11 @@
let (subscriber, handle) = subscriber::mock()
.new_span(expect::span().named("foo"))
.enter(expect::span().named("foo"))
- .new_span(expect::span().named("bar").with_explicit_parent(None))
+ .new_span(
+ expect::span()
+ .named("bar")
+ .with_ancestry(expect::is_explicit_root()),
+ )
.exit(expect::span().named("foo"))
.only()
.run_with_handle();
@@ -674,7 +683,7 @@
.new_span(
expect::span()
.named("bar")
- .with_explicit_parent(Some("foo")),
+ .with_ancestry(expect::has_explicit_parent("foo")),
)
.only()
.run_with_handle();
@@ -692,11 +701,31 @@
fn explicit_child_at_levels() {
let (subscriber, handle) = subscriber::mock()
.new_span(expect::span().named("foo"))
- .new_span(expect::span().named("a").with_explicit_parent(Some("foo")))
- .new_span(expect::span().named("b").with_explicit_parent(Some("foo")))
- .new_span(expect::span().named("c").with_explicit_parent(Some("foo")))
- .new_span(expect::span().named("d").with_explicit_parent(Some("foo")))
- .new_span(expect::span().named("e").with_explicit_parent(Some("foo")))
+ .new_span(
+ expect::span()
+ .named("a")
+ .with_ancestry(expect::has_explicit_parent("foo")),
+ )
+ .new_span(
+ expect::span()
+ .named("b")
+ .with_ancestry(expect::has_explicit_parent("foo")),
+ )
+ .new_span(
+ expect::span()
+ .named("c")
+ .with_ancestry(expect::has_explicit_parent("foo")),
+ )
+ .new_span(
+ expect::span()
+ .named("d")
+ .with_ancestry(expect::has_explicit_parent("foo")),
+ )
+ .new_span(
+ expect::span()
+ .named("e")
+ .with_ancestry(expect::has_explicit_parent("foo")),
+ )
.only()
.run_with_handle();
@@ -722,7 +751,7 @@
.new_span(
expect::span()
.named("baz")
- .with_explicit_parent(Some("foo")),
+ .with_ancestry(expect::has_explicit_parent("foo")),
)
.exit(expect::span().named("bar"))
.only()
@@ -741,7 +770,11 @@
#[test]
fn contextual_root() {
let (subscriber, handle) = subscriber::mock()
- .new_span(expect::span().named("foo").with_contextual_parent(None))
+ .new_span(
+ expect::span()
+ .named("foo")
+ .with_ancestry(expect::is_contextual_root()),
+ )
.only()
.run_with_handle();
@@ -761,7 +794,7 @@
.new_span(
expect::span()
.named("bar")
- .with_contextual_parent(Some("foo")),
+ .with_ancestry(expect::has_contextual_parent("foo")),
)
.exit(expect::span().named("foo"))
.only()
@@ -781,7 +814,7 @@
fn display_shorthand() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("my_span").with_field(
+ expect::span().named("my_span").with_fields(
expect::field("my_field")
.with_value(&display("hello world"))
.only(),
@@ -801,7 +834,7 @@
fn debug_shorthand() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("my_span").with_field(
+ expect::span().named("my_span").with_fields(
expect::field("my_field")
.with_value(&debug("hello world"))
.only(),
@@ -821,7 +854,7 @@
fn both_shorthands() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("my_span").with_field(
+ expect::span().named("my_span").with_fields(
expect::field("display_field")
.with_value(&display("hello world"))
.and(expect::field("debug_field").with_value(&debug("hello world")))
@@ -842,7 +875,7 @@
fn constant_field_name() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("my_span").with_field(
+ expect::span().named("my_span").with_fields(
expect::field("foo")
.with_value(&"bar")
.and(expect::field("constant string").with_value(&"also works"))
@@ -866,3 +899,20 @@
handle.assert_finished();
}
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn keyword_ident_in_field_name_span_macro() {
+ #[derive(Debug)]
+ struct Foo;
+
+ let (subscriber, handle) = subscriber::mock()
+ .new_span(expect::span().with_fields(expect::field("self").with_value(&debug(Foo)).only()))
+ .only()
+ .run_with_handle();
+
+ with_default(subscriber, || {
+ error_span!("span", self = ?Foo);
+ });
+ handle.assert_finished();
+}
diff --git a/crates/tracing/tests/subscriber.rs b/crates/tracing/tests/subscriber.rs
index f676efe..1b98628 100644
--- a/crates/tracing/tests/subscriber.rs
+++ b/crates/tracing/tests/subscriber.rs
@@ -60,7 +60,7 @@
fn boxed_subscriber() {
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
@@ -93,7 +93,7 @@
let (subscriber, handle) = subscriber::mock()
.new_span(
- expect::span().named("foo").with_field(
+ expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
diff --git a/crates/uuid/.android-checksum.json b/crates/uuid/.android-checksum.json
index 6be07f6..2110fe1 100644
--- a/crates/uuid/.android-checksum.json
+++ b/crates/uuid/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"c80812a1ec6a2a6a3f9957359095bf9c1e8cbcdae4a531775a783dd6266ed46d","Android.bp":"2bbcb0aaf870704ac5aecc4279bbed1d684ffa6747cf84a77daca1029e1a2338","Cargo.toml":"d735a31bccbca378268ebb919e4a742f885ea62643b5349a31d61ca83cf74a81","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"08bc387e24ed88cdda3af9eb97d401d69c31dc98049e63a9c3dfd763dc45a29d","METADATA":"8e9891c6939ba8ab0c456a22f5eefd4accc485ceb747b34d3631bd234ed84a7a","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"8f1b5b3db24ce08b2b90aa46eca37567be46d499a86a6ed5a4e108bae714b58c","TEST_MAPPING":"e93962d7a70966b064fcb2f8429feb4f97dca1cfaeb526f4ec56a44305c6de69","cargo_embargo.json":"3bf1a98721509650cb9f52cad8d9432fc466b69a9556fc24726caee458caaf0b","rules.mk":"c6a05070a68ecf74533aa73ac4cef3a8ada1f5ef3de138d4c98a79c48f863482","src/builder.rs":"bbfcd69aad7e4eaabe0e987febd3f6c5f30ff58d76701f8c890e42f641682546","src/error.rs":"a074f13c8ebaef5c563e961fae295134bf75386bfc44efbea5fefbbef7f44f24","src/external.rs":"d5b9021ea1db870ed29525e3290fae1d6ef89ab615d54548830c6632a1353d4c","src/external/arbitrary_support.rs":"f508e7015c8c14c8304a133eb275b60fb946a194c7e431642fe2fa0959a9990c","src/external/borsh_support.rs":"9b7f8438d29dad79dc79efd5db412e8a37e29d567ae21c76f4f3f37febabcd07","src/external/serde_support.rs":"15e735ff56d72b5a8bb846f429cf463361ba1e50d83118df27222ea01a8b0cb3","src/external/slog_support.rs":"9f7009bf65d8ca6961ad9b170c33a5eb787820155286a070324ff0d5fd3c09a2","src/fmt.rs":"80c5fa5eec2e2afcc4d8bfb045b63b3c796ac72fb0b7646d297cf35aa9eabd38","src/lib.rs":"2ebd885a33317ff2ca93be509b96816000799cc6bd5af9a3cf07f60270a341bc","src/macros.rs":"59262f55948d70b7ca0dcee15c990b6a3ad4fca47705d36ba1e90f88e53e8839","src/md5.rs":"5612c79b17b868220029baf52a87bff31530e607ac2522afdf8677eecb28e780","src/parser.rs":"a0d125589f80f3e30bca52897b58c2fce10473eecca1f2b5253ccc024f19162b","src/rng.rs":"158c5d6336c10007f7fa8ede586e7b2fccc9e28186109c8ca060f6e0086c5bb2","src/sha1.rs":"173b844e6a3975270ea36d235f5b7e406e9612ba95833d811b3602f5107589f0","src/timestamp.rs":"0e7db00a98d997bf8b3caa2ad3d97a892cecfcf56d5f19f34d3a392997292431","src/v1.rs":"e52a27d89e344036ffe4408a7b5b5fb326d6fb18ae06704a090c6399b7eb3611","src/v3.rs":"7af107a0c0339cae9b50b8b084c5339caf850fe0cd60bc1b129577ee2f140e67","src/v4.rs":"20ae5609a8f891dca1bd8fd99421121dfe452d9ce7f51d635a6d14b4a09889b5","src/v5.rs":"2e435575aa2f5f8971709ffac815407e5f245fe24f64a4666881549a516e30d8","src/v6.rs":"b0f8cf1bec6df06aa7c3d06a465c1a795f02a0bb41328b9509a4662c7b4cf89d","src/v7.rs":"13eb4cc951ac6359aa0d751172cc3f2155dc76a9c89e65d6d5a3f3817d0b2b7c","src/v8.rs":"25b49a2e5e947cffad78ac4b6da187b75469193814eb340eb8a1a9c644021106"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"dea2b2b850b17d4c3c2bcac38bde6c8c53a18291e647d02a91a331f31ec288e8","Android.bp":"737eccb7bd5d9b2d254e30efef3965fd6c8a5697565633e22653170ad316b146","Cargo.toml":"705581536ed46491230879ed499840673b339b20383b8755fe26874e2ffb6625","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"08bc387e24ed88cdda3af9eb97d401d69c31dc98049e63a9c3dfd763dc45a29d","METADATA":"35cf8ca93bdc132c2acc772c50768a274b9ec7d24094a6c4b0a8646bb2e59830","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"70396bc90fbfe2aad4750ba42c49de7e300741052693364ec602845d3968531f","TEST_MAPPING":"e93962d7a70966b064fcb2f8429feb4f97dca1cfaeb526f4ec56a44305c6de69","cargo_embargo.json":"3bf1a98721509650cb9f52cad8d9432fc466b69a9556fc24726caee458caaf0b","rules.mk":"c6a05070a68ecf74533aa73ac4cef3a8ada1f5ef3de138d4c98a79c48f863482","src/builder.rs":"be1a3145dec9b84852e0308443e09d5961a6f7278a574336a42a91babf83018d","src/error.rs":"7d2913559d5179ff0ed4430abe0fea081cd6033c1c89f366c6fe91984f1151cd","src/external.rs":"d5b9021ea1db870ed29525e3290fae1d6ef89ab615d54548830c6632a1353d4c","src/external/arbitrary_support.rs":"e012688d57194c3eb03393fc4c768bba0afd57667c3525f7b36027bf7ab546cd","src/external/borsh_support.rs":"9b7f8438d29dad79dc79efd5db412e8a37e29d567ae21c76f4f3f37febabcd07","src/external/serde_support.rs":"86e708f535cdc16a8ece6f6bdf579c3beee453a7346077263b8636ee1d1d3e50","src/external/slog_support.rs":"beaa766f1e536a65bbe983192feefc3db8510058d81bf098a8265bc33100180b","src/fmt.rs":"80c5fa5eec2e2afcc4d8bfb045b63b3c796ac72fb0b7646d297cf35aa9eabd38","src/lib.rs":"ab6849d33bc1f1f1eceb8b3a611e194f7c25b8129c4ff68f92b6940d8e1fb04b","src/macros.rs":"59262f55948d70b7ca0dcee15c990b6a3ad4fca47705d36ba1e90f88e53e8839","src/md5.rs":"5612c79b17b868220029baf52a87bff31530e607ac2522afdf8677eecb28e780","src/non_nil.rs":"b09342ba0f2a92123a0b3578132e54f5968d7ab40f877eadaf6274847de27074","src/parser.rs":"a0d125589f80f3e30bca52897b58c2fce10473eecca1f2b5253ccc024f19162b","src/rng.rs":"158c5d6336c10007f7fa8ede586e7b2fccc9e28186109c8ca060f6e0086c5bb2","src/sha1.rs":"173b844e6a3975270ea36d235f5b7e406e9612ba95833d811b3602f5107589f0","src/timestamp.rs":"170a2a94271cd6ec9a672c7baa1c7a0adf09e1de06acd80c3bc06cf35600b20b","src/v1.rs":"e52a27d89e344036ffe4408a7b5b5fb326d6fb18ae06704a090c6399b7eb3611","src/v3.rs":"7af107a0c0339cae9b50b8b084c5339caf850fe0cd60bc1b129577ee2f140e67","src/v4.rs":"20ae5609a8f891dca1bd8fd99421121dfe452d9ce7f51d635a6d14b4a09889b5","src/v5.rs":"2e435575aa2f5f8971709ffac815407e5f245fe24f64a4666881549a516e30d8","src/v6.rs":"b0f8cf1bec6df06aa7c3d06a465c1a795f02a0bb41328b9509a4662c7b4cf89d","src/v7.rs":"6aa389648dd187a86766e420d7b055c79ed1437353590c2440fe04f713a7b650","src/v8.rs":"25b49a2e5e947cffad78ac4b6da187b75469193814eb340eb8a1a9c644021106"}}
\ No newline at end of file
diff --git a/crates/uuid/.cargo-checksum.json b/crates/uuid/.cargo-checksum.json
index 0cad08a..deb142e 100644
--- a/crates/uuid/.cargo-checksum.json
+++ b/crates/uuid/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"3e129d39ba3ad1910e2492c75c2596faa848683660bcb0d443bd7b7a1a5ef726","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d","README.md":"383cbe6804afa72e448883bd0419087e2afd1e0ea7c5d66fdedfb228d1dc8e8a","src/builder.rs":"2f46ba8b4f9b333b0edc926a05c2a9a2d5bf3629cdad05a058532005e71ecc29","src/error.rs":"6a402cde971f7d186be3a184953bc6c89e8b8039fa95cd351e1e158fbe7378cb","src/external.rs":"a0640bdb98de1c24fcc9851a438a5abe6f7e3acb195885c817a64fac25521b9d","src/external/arbitrary_support.rs":"7e7fbcc4b8af5878b71858a1f5fa31e85d84fc2fd159614f8d450ba1fe06ac28","src/external/borsh_support.rs":"b49d82a59653445ba26db46a1515294b1ab480c0671dbe5499dfd1fb02588b3b","src/external/serde_support.rs":"b818f54e784143677146666a6737597bf95523d0de73fedc90b22d9c949b0997","src/external/slog_support.rs":"4fe1cc136b1eb5e27d8b308801bcd72872c129fd20ced1f2415af6760a83751b","src/fmt.rs":"3bf88d68d838bef81380a1e669a86eee46f24a8113effbd7b4e92da714ec97c7","src/lib.rs":"e005d6b2ffaa8e0e253a1e565878653bfb642dd4aa5d01cab5c2b368ca591880","src/macros.rs":"dff4a00bcbc37912d38d58edc3edfb8552ba8bb936403b8b33fe7dc3c2041908","src/md5.rs":"316d65b760ffa58beb6aa678be24359eb21a744e9e143bc99c11fe1907659245","src/parser.rs":"838e4a5af613a1d9b9dd6ca4b3c13a42e65fdea35fc02c93c34a416387dbdb7c","src/rng.rs":"d9cdd08ca225f28c103276c985db0540bb8db877a4bcb5348cb4a2648b29883e","src/sha1.rs":"e1a9657e11f1ed1ede33c0655f9c2641059b7c24f17be4ac425c930cc216e019","src/timestamp.rs":"b53c7ec88c4be2d73100d1300cfb684b3d0d93173f93e9fdcde54b75d463f661","src/v1.rs":"9c8254742e58a1d75b8374260108516fc914e2641f83e3a8ada75f05a62a62d1","src/v3.rs":"287860f5376e35d5292959d65948bdb0bbdb4605e3d2e463742c5400075bbe76","src/v4.rs":"c2f2844791cdb2c9e0c90bf7d9d155b96572f1f77aa9586104ddb77d44a5aeea","src/v5.rs":"70799f332c043b3d3ddf4aee791aa448296a5e05122be434945076f9cb29517c","src/v6.rs":"7bd0a52aa316e145ad55b99b0ad46ad3234b0936ab61a4935300f053f2030a56","src/v7.rs":"3ec7d26bbb457347f4c282f3feb90f123d18e62f81753b81196c995034c28256","src/v8.rs":"15a4c3b81afcca4ec406192f2099fac0ad43d734e12672b02a693ddcc38b6684"},"package":"b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4"}
\ No newline at end of file
+{"files":{"Cargo.toml":"286a84f2f8c07e743b2d4af0055f1723763a4175c1dc8c4d60e717f3662e7738","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d","README.md":"151c7172b60dab055ec853b7534a2b1f5becafd70c3478d558fc578dd98ed317","src/builder.rs":"855c402ff7d6ac920ddc89fd75d365818187529970fdea17e3af32f255ea8b4e","src/error.rs":"2d1c71fe562fe695cd690e6e2371f998424472bfb8c5e31bc36d953d5523040b","src/external.rs":"a0640bdb98de1c24fcc9851a438a5abe6f7e3acb195885c817a64fac25521b9d","src/external/arbitrary_support.rs":"a7e0f0686c7c79e3af797f6d3f1976bee13fa9c3d8cb40f58bd10ff6ebdadae3","src/external/borsh_support.rs":"b49d82a59653445ba26db46a1515294b1ab480c0671dbe5499dfd1fb02588b3b","src/external/serde_support.rs":"4275abf9b543e5a1ebc20823208d0a983e5e34131da4d41648b4685f4a81df63","src/external/slog_support.rs":"d9bab6f91a80c773e77326ce40d84976beb2b2946ab708de21f25afe5b9ece13","src/fmt.rs":"3bf88d68d838bef81380a1e669a86eee46f24a8113effbd7b4e92da714ec97c7","src/lib.rs":"3166822eef37e2a67fc9eccf2639bda04d83350ff0a40181e1dfe8a932d10624","src/macros.rs":"dff4a00bcbc37912d38d58edc3edfb8552ba8bb936403b8b33fe7dc3c2041908","src/md5.rs":"316d65b760ffa58beb6aa678be24359eb21a744e9e143bc99c11fe1907659245","src/non_nil.rs":"a1cfe91f63cad13ef4d0a9e8e4c9aad55b8b29ec67c6c3897e8093d669d890fe","src/parser.rs":"838e4a5af613a1d9b9dd6ca4b3c13a42e65fdea35fc02c93c34a416387dbdb7c","src/rng.rs":"d9cdd08ca225f28c103276c985db0540bb8db877a4bcb5348cb4a2648b29883e","src/sha1.rs":"e1a9657e11f1ed1ede33c0655f9c2641059b7c24f17be4ac425c930cc216e019","src/timestamp.rs":"ad68e5b71953fb9df544fad858f9bde878a7d483f6a273523d8ee073511078d5","src/v1.rs":"9c8254742e58a1d75b8374260108516fc914e2641f83e3a8ada75f05a62a62d1","src/v3.rs":"287860f5376e35d5292959d65948bdb0bbdb4605e3d2e463742c5400075bbe76","src/v4.rs":"c2f2844791cdb2c9e0c90bf7d9d155b96572f1f77aa9586104ddb77d44a5aeea","src/v5.rs":"70799f332c043b3d3ddf4aee791aa448296a5e05122be434945076f9cb29517c","src/v6.rs":"7bd0a52aa316e145ad55b99b0ad46ad3234b0936ab61a4935300f053f2030a56","src/v7.rs":"bc737c502247c83189f3627c7f1612e0af2638c5919d47aeffee3647b9ec63a0","src/v8.rs":"15a4c3b81afcca4ec406192f2099fac0ad43d734e12672b02a693ddcc38b6684"},"package":"744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"}
\ No newline at end of file
diff --git a/crates/uuid/Android.bp b/crates/uuid/Android.bp
index 7642834..867c937 100644
--- a/crates/uuid/Android.bp
+++ b/crates/uuid/Android.bp
@@ -18,7 +18,7 @@
host_supported: true,
crate_name: "uuid",
cargo_env_compat: true,
- cargo_pkg_version: "1.11.1",
+ cargo_pkg_version: "1.12.0",
crate_root: "src/lib.rs",
edition: "2018",
features: [
@@ -47,7 +47,7 @@
host_supported: true,
crate_name: "uuid",
cargo_env_compat: true,
- cargo_pkg_version: "1.11.1",
+ cargo_pkg_version: "1.12.0",
crate_root: "src/lib.rs",
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -81,7 +81,7 @@
name: "libuuid_nostd",
crate_name: "uuid",
cargo_env_compat: true,
- cargo_pkg_version: "1.11.1",
+ cargo_pkg_version: "1.12.0",
crate_root: "src/lib.rs",
edition: "2018",
apex_available: [
diff --git a/crates/uuid/Cargo.toml b/crates/uuid/Cargo.toml
index 0065894..0882340 100644
--- a/crates/uuid/Cargo.toml
+++ b/crates/uuid/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2018"
rust-version = "1.63.0"
name = "uuid"
-version = "1.11.1"
+version = "1.12.0"
authors = [
"Ashley Mannix<[email protected]>",
"Dylan DPC<[email protected]>",
@@ -141,7 +141,7 @@
optional = true
[dependencies.uuid-macro-internal]
-version = "1.11.1"
+version = "1.12.0"
optional = true
[dependencies.zerocopy]
diff --git a/crates/uuid/METADATA b/crates/uuid/METADATA
index 80f6f51..754e4f6 100644
--- a/crates/uuid/METADATA
+++ b/crates/uuid/METADATA
@@ -1,17 +1,17 @@
name: "uuid"
description: "A library to generate and parse UUIDs."
third_party {
- version: "1.11.1"
+ version: "1.12.0"
license_type: NOTICE
last_upgrade_date {
year: 2025
month: 1
- day: 13
+ day: 14
}
homepage: "https://crates.io/crates/uuid"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/uuid/uuid-1.11.1.crate"
- version: "1.11.1"
+ value: "https://static.crates.io/crates/uuid/uuid-1.12.0.crate"
+ version: "1.12.0"
}
}
diff --git a/crates/uuid/README.md b/crates/uuid/README.md
index bc85103..f76e2fd 100644
--- a/crates/uuid/README.md
+++ b/crates/uuid/README.md
@@ -28,7 +28,7 @@
```toml
[dependencies.uuid]
-version = "1.11.1"
+version = "1.12.0"
features = [
"v4", # Lets you generate random UUIDs
"fast-rng", # Use a faster (but still sufficiently random) RNG
@@ -65,11 +65,11 @@
If you'd like to parse UUIDs _really_ fast, check out the [`uuid-simd`](https://github.com/nugine/uuid-simd)
library.
-For more details on using `uuid`, [see the library documentation](https://docs.rs/uuid/1.11.1/uuid).
+For more details on using `uuid`, [see the library documentation](https://docs.rs/uuid/1.12.0/uuid).
## References
-* [`uuid` library docs](https://docs.rs/uuid/1.11.1/uuid).
+* [`uuid` library docs](https://docs.rs/uuid/1.12.0/uuid).
* [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier).
* [RFC 9562: Universally Unique IDentifiers (UUID)](https://www.ietf.org/rfc/rfc9562.html).
diff --git a/crates/uuid/src/builder.rs b/crates/uuid/src/builder.rs
index f1b6811..6a07a30 100644
--- a/crates/uuid/src/builder.rs
+++ b/crates/uuid/src/builder.rs
@@ -909,7 +909,7 @@
#[doc(hidden)]
impl Builder {
#[deprecated(
- since = "1.11.1",
+ since = "1.10.0",
note = "use `Builder::from_gregorian_timestamp(ticks, counter, node_id)`"
)]
pub const fn from_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Self {
@@ -917,7 +917,7 @@
}
#[deprecated(
- since = "1.11.1",
+ since = "1.10.0",
note = "use `Builder::from_sorted_gregorian_timestamp(ticks, counter, node_id)`"
)]
pub const fn from_sorted_rfc4122_timestamp(
diff --git a/crates/uuid/src/error.rs b/crates/uuid/src/error.rs
index 30e0175..7a65c2d 100644
--- a/crates/uuid/src/error.rs
+++ b/crates/uuid/src/error.rs
@@ -30,6 +30,8 @@
},
/// The input was not a valid UTF8 string
InvalidUTF8,
+ /// The UUID is nil.
+ Nil,
/// Some other error occurred.
Other,
}
@@ -158,6 +160,7 @@
)
}
ErrorKind::InvalidUTF8 => write!(f, "non-UTF8 input"),
+ ErrorKind::Nil => write!(f, "the UUID is nil"),
ErrorKind::Other => write!(f, "failed to parse a UUID"),
}
}
diff --git a/crates/uuid/src/external/arbitrary_support.rs b/crates/uuid/src/external/arbitrary_support.rs
index 40c11f5..c753f74 100644
--- a/crates/uuid/src/external/arbitrary_support.rs
+++ b/crates/uuid/src/external/arbitrary_support.rs
@@ -1,4 +1,8 @@
-use crate::{std::convert::TryInto, Builder, Uuid};
+use crate::{
+ non_nil::NonNilUuid,
+ std::convert::{TryFrom, TryInto},
+ Builder, Uuid,
+};
use arbitrary::{Arbitrary, Unstructured};
@@ -16,6 +20,16 @@
(16, Some(16))
}
}
+impl arbitrary::Arbitrary<'_> for NonNilUuid {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
+ let uuid = Uuid::arbitrary(u)?;
+ Self::try_from(uuid).map_err(|_| arbitrary::Error::IncorrectFormat)
+ }
+
+ fn size_hint(_: usize) -> (usize, Option<usize>) {
+ (16, Some(16))
+ }
+}
#[cfg(test)]
mod tests {
@@ -42,4 +56,16 @@
assert!(uuid.is_err());
}
+
+ #[test]
+ fn test_arbitrary_non_nil() {
+ let mut bytes = Unstructured::new(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ let non_nil_uuid = NonNilUuid::arbitrary(&mut bytes).unwrap();
+ let uuid: Uuid = non_nil_uuid.into();
+
+ assert_eq!(Some(Version::Random), uuid.get_version());
+ assert_eq!(Variant::RFC4122, uuid.get_variant());
+ assert!(!uuid.is_nil());
+ }
}
diff --git a/crates/uuid/src/external/serde_support.rs b/crates/uuid/src/external/serde_support.rs
index f389271..954a349 100644
--- a/crates/uuid/src/external/serde_support.rs
+++ b/crates/uuid/src/external/serde_support.rs
@@ -10,8 +10,10 @@
// except according to those terms.
use crate::{
+ convert::TryFrom,
error::*,
fmt::{Braced, Hyphenated, Simple, Urn},
+ non_nil::NonNilUuid,
std::fmt,
Uuid,
};
@@ -30,6 +32,15 @@
}
}
+impl Serialize for NonNilUuid {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ Uuid::from(*self).serialize(serializer)
+ }
+}
+
impl Serialize for Hyphenated {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.encode_lower(&mut Uuid::encode_buffer()))
@@ -127,6 +138,17 @@
}
}
+impl<'de> Deserialize<'de> for NonNilUuid {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let uuid = Uuid::deserialize(deserializer)?;
+
+ NonNilUuid::try_from(uuid).map_err(|_| de::Error::invalid_value(de::Unexpected::Other("nil UUID"), &"a non-nil UUID"))
+ }
+}
+
enum ExpectedFormat {
Simple,
Braced,
@@ -732,4 +754,14 @@
"UUID parsing failed: invalid length: expected 16 bytes, found 11",
);
}
+
+ #[test]
+ fn test_serde_non_nil_uuid() {
+ let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
+ let uuid = Uuid::parse_str(uuid_str).unwrap();
+ let non_nil_uuid = NonNilUuid::try_from(uuid).unwrap();
+
+ serde_test::assert_ser_tokens(&non_nil_uuid.readable(), &[Token::Str(uuid_str)]);
+ serde_test::assert_de_tokens(&non_nil_uuid.readable(), &[Token::Str(uuid_str)]);
+ }
}
diff --git a/crates/uuid/src/external/slog_support.rs b/crates/uuid/src/external/slog_support.rs
index cb06255..7e1c419 100644
--- a/crates/uuid/src/external/slog_support.rs
+++ b/crates/uuid/src/external/slog_support.rs
@@ -9,7 +9,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use crate::Uuid;
+use crate::{non_nil::NonNilUuid, Uuid};
impl slog::Value for Uuid {
fn serialize(
@@ -22,6 +22,17 @@
}
}
+impl slog::Value for NonNilUuid {
+ fn serialize(
+ &self,
+ record: &slog::Record<'_>,
+ key: slog::Key,
+ serializer: &mut dyn slog::Serializer,
+ ) -> Result<(), slog::Error> {
+ Uuid::from(*self).serialize(record, key, serializer)
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::tests::new;
diff --git a/crates/uuid/src/lib.rs b/crates/uuid/src/lib.rs
index 7d9ed42..7843d7d 100644
--- a/crates/uuid/src/lib.rs
+++ b/crates/uuid/src/lib.rs
@@ -38,7 +38,7 @@
//!
//! ```toml
//! [dependencies.uuid]
-//! version = "1.11.1"
+//! version = "1.12.0"
//! features = [
//! "v4", # Lets you generate random UUIDs
//! "fast-rng", # Use a faster (but still sufficiently random) RNG
@@ -138,7 +138,7 @@
//!
//! ```toml
//! [dependencies.uuid]
-//! version = "1.11.1"
+//! version = "1.12.0"
//! features = [
//! "v4",
//! "v7",
@@ -153,7 +153,7 @@
//!
//! ```toml
//! [dependencies.uuid]
-//! version = "1.11.1"
+//! version = "1.12.0"
//! default-features = false
//! ```
//!
@@ -211,7 +211,7 @@
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
- html_root_url = "https://docs.rs/uuid/1.11.1"
+ html_root_url = "https://docs.rs/uuid/1.12.0"
)]
#[cfg(any(feature = "std", test))]
@@ -222,11 +222,9 @@
#[macro_use]
extern crate core as std;
-#[cfg(all(uuid_unstable, feature = "zerocopy"))]
-use zerocopy::{IntoBytes, FromBytes, Immutable, KnownLayout, Unaligned};
-
mod builder;
mod error;
+mod non_nil;
mod parser;
pub mod fmt;
@@ -281,7 +279,7 @@
use crate::std::convert;
-pub use crate::{builder::Builder, error::Error};
+pub use crate::{builder::Builder, error::Error, non_nil::NonNilUuid};
/// A 128-bit (16 byte) buffer containing the UUID.
///
@@ -436,15 +434,16 @@
///
/// The `Uuid` type is always guaranteed to be have the same ABI as [`Bytes`].
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
+#[repr(transparent)]
+// NOTE: Also check `NonNilUuid` when ading new derives here
#[cfg_attr(
all(uuid_unstable, feature = "zerocopy"),
- derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned)
+ derive(zerocopy::IntoBytes, zerocopy::FromBytes, zerocopy::KnownLayout, zerocopy::Immutable, zerocopy::Unaligned)
)]
#[cfg_attr(
feature = "borsh",
derive(borsh_derive::BorshDeserialize, borsh_derive::BorshSerialize)
)]
-#[repr(transparent)]
#[cfg_attr(
feature = "bytemuck",
derive(bytemuck::Zeroable, bytemuck::Pod, bytemuck::TransparentWrapper)
diff --git a/crates/uuid/src/non_nil.rs b/crates/uuid/src/non_nil.rs
new file mode 100644
index 0000000..ee235a1
--- /dev/null
+++ b/crates/uuid/src/non_nil.rs
@@ -0,0 +1,142 @@
+//! A wrapper type for nil UUIDs that provides a more memory-efficient
+//! `Option<NonNilUuid>` representation.
+
+use core::convert::TryFrom;
+use std::{fmt, num::NonZeroU128};
+
+use crate::{
+ error::{Error, ErrorKind},
+ Uuid,
+};
+
+/// A UUID that is guaranteed not to be the [nil UUID](https://www.ietf.org/rfc/rfc9562.html#name-nil-uuid).
+///
+/// This is useful for representing optional UUIDs more efficiently, as `Option<NonNilUuid>`
+/// takes up the same space as `Uuid`.
+///
+/// Note that `Uuid`s created by the following methods are guaranteed to be non-nil:
+///
+/// - [`Uuid::new_v1`]
+/// - [`Uuid::now_v1`]
+/// - [`Uuid::new_v3`]
+/// - [`Uuid::new_v4`]
+/// - [`Uuid::new_v5`]
+/// - [`Uuid::new_v6`]
+/// - [`Uuid::now_v6`]
+/// - [`Uuid::new_v7`]
+/// - [`Uuid::now_v7`]
+/// - [`Uuid::new_v8`]
+///
+/// # ABI
+///
+/// The `NonNilUuid` type does not yet have a stable ABI. Its representation or alignment
+/// may change. It is currently only guaranteed that `NonNilUuid` and `Option<NonNilUuid>`
+/// are the same size as `Uuid`.
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct NonNilUuid(NonZeroU128);
+
+impl fmt::Display for NonNilUuid {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", Uuid::from(*self))
+ }
+}
+
+impl PartialEq<Uuid> for NonNilUuid {
+ fn eq(&self, other: &Uuid) -> bool {
+ self.get() == *other
+ }
+}
+
+impl PartialEq<NonNilUuid> for Uuid {
+ fn eq(&self, other: &NonNilUuid) -> bool {
+ *self == other.get()
+ }
+}
+
+impl NonNilUuid {
+ /// Creates a non-nil UUID if the value is non-nil.
+ pub const fn new(uuid: Uuid) -> Option<Self> {
+ match NonZeroU128::new(uuid.as_u128()) {
+ Some(non_nil) => Some(NonNilUuid(non_nil)),
+ None => None,
+ }
+ }
+
+ /// Creates a non-nil without checking whether the value is non-nil. This results in undefined behavior if the value is nil.
+ ///
+ /// # Safety
+ ///
+ /// The value must not be nil.
+ pub const unsafe fn new_unchecked(uuid: Uuid) -> Self {
+ NonNilUuid(unsafe { NonZeroU128::new_unchecked(uuid.as_u128()) })
+ }
+
+ /// Get the underlying [`Uuid`] value.
+ #[inline]
+ pub const fn get(self) -> Uuid {
+ Uuid::from_u128(self.0.get())
+ }
+}
+
+impl From<NonNilUuid> for Uuid {
+ /// Converts a [`NonNilUuid`] back into a [`Uuid`].
+ ///
+ /// # Examples
+ /// ```
+ /// # use std::convert::TryFrom;
+ /// # use uuid::{NonNilUuid, Uuid};
+ /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
+ /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
+ /// let uuid_again = Uuid::from(non_nil);
+ ///
+ /// assert_eq!(uuid, uuid_again);
+ /// ```
+ fn from(non_nil: NonNilUuid) -> Self {
+ Uuid::from_u128(non_nil.0.get())
+ }
+}
+
+impl TryFrom<Uuid> for NonNilUuid {
+ type Error = Error;
+
+ /// Attempts to convert a [`Uuid`] into a [`NonNilUuid`].
+ ///
+ /// # Examples
+ /// ```
+ /// # use std::convert::TryFrom;
+ /// # use uuid::{NonNilUuid, Uuid};
+ /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
+ /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
+ /// ```
+ fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
+ NonZeroU128::new(uuid.as_u128())
+ .map(Self)
+ .ok_or(Error(ErrorKind::Nil))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_non_nil_with_option_size() {
+ assert_eq!(
+ std::mem::size_of::<Option<NonNilUuid>>(),
+ std::mem::size_of::<Uuid>()
+ );
+ }
+
+ #[test]
+ fn test_non_nil() {
+ let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
+
+ assert_eq!(Uuid::from(NonNilUuid::try_from(uuid).unwrap()), uuid);
+ assert_eq!(NonNilUuid::new(uuid).unwrap(), uuid);
+ assert_eq!(unsafe { NonNilUuid::new_unchecked(uuid) }, uuid);
+
+ assert!(NonNilUuid::try_from(Uuid::nil()).is_err());
+ assert!(NonNilUuid::new(Uuid::nil()).is_none());
+ }
+}
diff --git a/crates/uuid/src/timestamp.rs b/crates/uuid/src/timestamp.rs
index 56262f1..d30df5b 100644
--- a/crates/uuid/src/timestamp.rs
+++ b/crates/uuid/src/timestamp.rs
@@ -128,7 +128,7 @@
///
/// If conversion from the internal timestamp format to ticks would overflow
/// then it will wrap.
- ///
+ ///
/// If the internal counter is wider than 14 bits then it will be truncated to 14 bits.
pub const fn to_gregorian(&self) -> (u64, u16) {
(
@@ -165,17 +165,23 @@
#[doc(hidden)]
impl Timestamp {
- #[deprecated(since = "1.11.1", note = "use `Timestamp::from_gregorian(ticks, counter)`")]
+ #[deprecated(
+ since = "1.10.0",
+ note = "use `Timestamp::from_gregorian(ticks, counter)`"
+ )]
pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
Timestamp::from_gregorian(ticks, counter)
}
- #[deprecated(since = "1.11.1", note = "use `Timestamp::to_gregorian()`")]
+ #[deprecated(since = "1.10.0", note = "use `Timestamp::to_gregorian()`")]
pub const fn to_rfc4122(&self) -> (u64, u16) {
self.to_gregorian()
}
- #[deprecated(since = "1.2.0", note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")]
+ #[deprecated(
+ since = "1.2.0",
+ note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`"
+ )]
pub const fn to_unix_nanos(&self) -> u32 {
panic!("`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")
}
diff --git a/crates/uuid/src/v7.rs b/crates/uuid/src/v7.rs
index c2b4a22..d55cf94 100644
--- a/crates/uuid/src/v7.rs
+++ b/crates/uuid/src/v7.rs
@@ -31,7 +31,7 @@
/// # Examples
///
/// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit
- /// random number. When supplied as such, the data will be combined
+ /// random number. When supplied as such, the data will be combined
/// to ensure uniqueness and sortability at millisecond granularity.
///
/// ```rust
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 4034e94..a496ef7 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",
@@ -338,7 +338,7 @@
"predicates",
"predicates-core",
"predicates-tree",
- "prettyplease 0.2.25",
+ "prettyplease 0.2.29",
"proc-macro2 1.0.93",
"protobuf 3.2.0",
"protobuf-codegen",
@@ -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",
@@ -890,7 +890,7 @@
"lazy_static",
"lazycell",
"log",
- "prettyplease 0.2.25",
+ "prettyplease 0.2.29",
"proc-macro2 1.0.93",
"quote 1.0.38",
"regex",
@@ -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"
@@ -3763,7 +3753,7 @@
"heck",
"pest",
"pest_derive",
- "prettyplease 0.2.25",
+ "prettyplease 0.2.29",
"proc-macro2 1.0.93",
"quote 1.0.38",
"serde",
@@ -4063,9 +4053,9 @@
[[package]]
name = "prettyplease"
-version = "0.2.25"
+version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
dependencies = [
"proc-macro2 1.0.93",
"syn 2.0.96",
@@ -5315,7 +5305,7 @@
"serde",
"serde_spanned",
"toml_datetime",
- "toml_edit 0.22.4",
+ "toml_edit 0.22.20",
]
[[package]]
@@ -5335,7 +5325,7 @@
dependencies = [
"indexmap 2.4.0",
"toml_datetime",
- "winnow",
+ "winnow 0.5.37",
]
[[package]]
@@ -5346,20 +5336,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]]
@@ -5423,9 +5413,9 @@
[[package]]
name = "tracing"
-version = "0.1.40"
+version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
@@ -5796,9 +5786,9 @@
[[package]]
name = "uuid"
-version = "1.11.1"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4"
+checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
[[package]]
name = "valuable"
@@ -6461,6 +6451,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 b2f861a..35020fc 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"
@@ -253,7 +253,7 @@
predicates = "=3.1.3"
predicates-core = "=1.0.9"
predicates-tree = "=1.0.11"
-prettyplease = "=0.2.25"
+prettyplease = "=0.2.29"
proc-macro2 = "=1.0.93"
protobuf = "=3.2.0"
protobuf-codegen = "=3.2.0"
@@ -337,12 +337,12 @@
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"
tower-service = "=0.3.3"
-tracing = "=0.1.40"
+tracing = "=0.1.41"
tracing-attributes = "=0.1.28"
tracing-core = "=0.1.33"
tracing-subscriber = "=0.3.19"
@@ -372,7 +372,7 @@
userfaultfd = "=0.8.1"
userfaultfd-sys = "=0.5.0"
utf-8 = "=0.7.6"
-uuid = "=1.11.1"
+uuid = "=1.12.0"
vhost = "=0.8.1"
vhost-user-backend = "=0.10.1"
virtio-bindings = "=0.2.2"
@@ -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"