diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:47:55 +0000 |
commit | 2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch) | |
tree | 033cc839730fda84ff08db877037977be94e5e3a /vendor/winnow | |
parent | Initial commit. (diff) | |
download | cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.tar.xz cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.zip |
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/winnow')
87 files changed, 28721 insertions, 0 deletions
diff --git a/vendor/winnow/.cargo-checksum.json b/vendor/winnow/.cargo-checksum.json new file mode 100644 index 0000000..bea9247 --- /dev/null +++ b/vendor/winnow/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{},"package":"a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c"}
\ No newline at end of file diff --git a/vendor/winnow/Cargo.lock b/vendor/winnow/Cargo.lock new file mode 100644 index 0000000..6a1e82b --- /dev/null +++ b/vendor/winnow/Cargo.lock @@ -0,0 +1,1415 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[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 = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytecount" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "circular" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea" + +[[package]] +name = "clap" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +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", +] + +[[package]] +name = "escargot" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6" +dependencies = [ + "log", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "handlebars" +version = "4.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.1", + "rustix 0.38.6", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexopt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "os_pipe" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] + +[[package]] +name = "pest" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pest_meta" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "plotters" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_assertions" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +dependencies = [ + "ctor", + "diff", + "output_vt100", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.6.29", + "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 = "quick-xml" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys 0.48.0", +] + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.22", +] + +[[package]] +name = "serde_json" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "similar" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" + +[[package]] +name = "snapbox" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835" +dependencies = [ + "anstream", + "anstyle", + "escargot", + "normalize-line-endings", + "similar", + "snapbox-macros", +] + +[[package]] +name = "snapbox-macros" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31" +dependencies = [ + "anstream", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix 0.37.20", + "windows-sys 0.48.0", +] + +[[package]] +name = "term-transcript" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d77280c9041d978e4ffedbdd19f710a1360085510c5e99dbcfec8e2f2bd4285" +dependencies = [ + "atty", + "bytecount", + "handlebars", + "os_pipe", + "pretty_assertions", + "quick-xml", + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +dependencies = [ + "rustix 0.37.20", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.22", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.22", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +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" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +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.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.5.17" +dependencies = [ + "anstream", + "anstyle", + "circular", + "criterion", + "doc-comment", + "escargot", + "is-terminal", + "lexopt", + "memchr", + "proptest", + "snapbox", + "term-transcript", + "terminal_size", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/vendor/winnow/Cargo.toml b/vendor/winnow/Cargo.toml new file mode 100644 index 0000000..a587cc8 --- /dev/null +++ b/vendor/winnow/Cargo.toml @@ -0,0 +1,219 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.64.0" +name = "winnow" +version = "0.5.17" +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "Cargo.lock", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*", +] +autoexamples = false +description = "A byte-oriented, zero-copy, parser combinators library" +readme = "README.md" +keywords = [ + "parser", + "parser-combinators", + "parsing", + "streaming", + "bit", +] +categories = ["parsing"] +license = "MIT" +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", +] + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{version}}" +search = "Unreleased" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = "...{{tag_name}}" +search = '\.\.\.HEAD' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{date}}" +search = "ReleaseDate" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ +<!-- next-header --> +## [Unreleased] - ReleaseDate +""" +search = "<!-- next-header -->" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ +<!-- next-url --> +[Unreleased]: https://github.com/winnow-rs/winnow/compare/{{tag_name}}...HEAD""" +search = "<!-- next-url -->" + +[profile.bench] +lto = true +codegen-units = 1 +debug = 2 + +[[example]] +name = "arithmetic" +test = true +required-features = ["alloc"] + +[[example]] +name = "css" +test = true +required-features = ["alloc"] + +[[example]] +name = "custom_error" +test = true +required-features = ["alloc"] + +[[example]] +name = "http" +required-features = ["alloc"] + +[[example]] +name = "ini" +test = true +required-features = ["std"] + +[[example]] +name = "json" +test = true +required-features = ["std"] + +[[example]] +name = "ndjson" +test = true +required-features = ["std"] + +[[example]] +name = "json_iterator" +required-features = ["std"] + +[[example]] +name = "iterator" + +[[example]] +name = "s_expression" +required-features = ["alloc"] + +[[example]] +name = "string" +required-features = ["alloc"] + +[[bench]] +name = "arithmetic" +path = "examples/arithmetic/bench.rs" +harness = false +required-features = ["alloc"] + +[[bench]] +name = "contains_token" +harness = false + +[[bench]] +name = "iter" +harness = false + +[[bench]] +name = "number" +harness = false + +[[bench]] +name = "http" +path = "examples/http/bench.rs" +harness = false +required-features = ["alloc"] + +[[bench]] +name = "ini" +path = "examples/ini/bench.rs" +harness = false +required-features = ["std"] + +[[bench]] +name = "json" +path = "examples/json/bench.rs" +harness = false +required-features = ["std"] + +[dependencies.memchr] +version = "2.5" +optional = true +default-features = false + +[dev-dependencies.circular] +version = "0.3.0" + +[dev-dependencies.criterion] +version = "0.5.1" + +[dev-dependencies.doc-comment] +version = "0.3" + +[dev-dependencies.escargot] +version = "0.5.7" + +[dev-dependencies.lexopt] +version = "0.3.0" + +[dev-dependencies.proptest] +version = "1.2.0" + +[dev-dependencies.snapbox] +version = "0.4.11" +features = ["examples"] + +[dev-dependencies.term-transcript] +version = "0.2.0" + +[features] +alloc = [] +default = ["std"] +simd = ["dep:memchr"] +std = [ + "alloc", + "memchr?/std", +] +unstable-doc = [ + "alloc", + "std", + "simd", +] diff --git a/vendor/winnow/LICENSE-MIT b/vendor/winnow/LICENSE-MIT new file mode 100644 index 0000000..c9b44cb --- /dev/null +++ b/vendor/winnow/LICENSE-MIT @@ -0,0 +1,18 @@ +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/winnow/README.md b/vendor/winnow/README.md new file mode 100644 index 0000000..507df4e --- /dev/null +++ b/vendor/winnow/README.md @@ -0,0 +1,27 @@ +# winnow, making parsing a breeze + +[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![Build Status](https://github.com/winnow-rs/winnow/actions/workflows/ci.yml/badge.svg)](https://github.com/winnow-rs/winnow/actions/workflows/ci.yml) +[![Coverage Status](https://coveralls.io/repos/github/winnow-rs/winnow/badge.svg?branch=main)](https://coveralls.io/github/winnow-rs/winnow?branch=main) +[![Crates.io Version](https://img.shields.io/crates/v/winnow.svg)](https://crates.io/crates/winnow) + +## About + +Build up a parser for your format of choice with the building blocks provided by `winnow`. + +For more details, see: +- [Tutorial](https://docs.rs/winnow/latest/winnow/_tutorial/index.html) +- [Special Topics](https://docs.rs/winnow/latest/winnow/_topic/index.html) + - [Why winnow? How does it compare to ...?](https://docs.rs/winnow/latest/winnow/_topic/why/index.html) +- [API Reference](https://docs.rs/winnow) +- [List of combinators](https://docs.rs/winnow/latest/winnow/combinator/index.html) + +# Contributors + +winnow is the fruit of the work of many contributors over the years, many +thanks for your help! In particular, thanks to [Geal](https://github.com/Geal) +for the original [`nom` crate](https://crates.io/crates/nom). + +<a href="https://github.com/winnow-rs/winnow/graphs/contributors"> + <img src="https://contributors-img.web.app/image?repo=winnow-rs/winnow" /> +</a> diff --git a/vendor/winnow/benches/contains_token.rs b/vendor/winnow/benches/contains_token.rs new file mode 100644 index 0000000..2980ce6 --- /dev/null +++ b/vendor/winnow/benches/contains_token.rs @@ -0,0 +1,94 @@ +use criterion::black_box; + +use winnow::combinator::alt; +use winnow::combinator::repeat; +use winnow::prelude::*; +use winnow::token::take_till1; +use winnow::token::take_while; + +fn contains_token(c: &mut criterion::Criterion) { + let data = [ + ("contiguous", CONTIGUOUS), + ("interleaved", INTERLEAVED), + ("canada", CANADA), + ]; + let mut group = c.benchmark_group("contains_token"); + for (name, sample) in data { + let len = sample.len(); + group.throughput(criterion::Throughput::Bytes(len as u64)); + + group.bench_with_input(criterion::BenchmarkId::new("slice", name), &len, |b, _| { + b.iter(|| black_box(parser_slice.parse_peek(black_box(sample)).unwrap())); + }); + group.bench_with_input(criterion::BenchmarkId::new("array", name), &len, |b, _| { + b.iter(|| black_box(parser_array.parse_peek(black_box(sample)).unwrap())); + }); + group.bench_with_input(criterion::BenchmarkId::new("tuple", name), &len, |b, _| { + b.iter(|| black_box(parser_tuple.parse_peek(black_box(sample)).unwrap())); + }); + group.bench_with_input( + criterion::BenchmarkId::new("closure-or", name), + &len, + |b, _| { + b.iter(|| black_box(parser_closure_or.parse_peek(black_box(sample)).unwrap())); + }, + ); + group.bench_with_input( + criterion::BenchmarkId::new("closure-matches", name), + &len, + |b, _| { + b.iter(|| { + black_box( + parser_closure_matches + .parse_peek(black_box(sample)) + .unwrap(), + ) + }); + }, + ); + } + group.finish(); +} + +fn parser_slice(input: &mut &str) -> PResult<usize> { + let contains = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'][..]; + repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) +} + +fn parser_array(input: &mut &str) -> PResult<usize> { + let contains = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) +} + +fn parser_tuple(input: &mut &str) -> PResult<usize> { + let contains = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); + repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) +} + +fn parser_closure_or(input: &mut &str) -> PResult<usize> { + let contains = |c: char| { + c == '0' + || c == '1' + || c == '2' + || c == '3' + || c == '4' + || c == '5' + || c == '6' + || c == '7' + || c == '8' + || c == '9' + }; + repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) +} + +fn parser_closure_matches(input: &mut &str) -> PResult<usize> { + let contains = |c: char| matches!(c, '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'); + repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) +} + +const CONTIGUOUS: &str = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; +const INTERLEAVED: &str = "0123456789abc0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab"; +const CANADA: &str = include_str!("../third_party/nativejson-benchmark/data/canada.json"); + +criterion::criterion_group!(benches, contains_token); +criterion::criterion_main!(benches); diff --git a/vendor/winnow/benches/iter.rs b/vendor/winnow/benches/iter.rs new file mode 100644 index 0000000..0a0d5ff --- /dev/null +++ b/vendor/winnow/benches/iter.rs @@ -0,0 +1,120 @@ +use criterion::black_box; + +use winnow::combinator::opt; +use winnow::prelude::*; +use winnow::stream::AsChar; +use winnow::stream::Stream as _; +use winnow::token::one_of; + +fn iter(c: &mut criterion::Criterion) { + let data = [ + ("contiguous", CONTIGUOUS.as_bytes()), + ("interleaved", INTERLEAVED.as_bytes()), + ("canada", CANADA.as_bytes()), + ]; + let mut group = c.benchmark_group("iter"); + for (name, sample) in data { + let len = sample.len(); + group.throughput(criterion::Throughput::Bytes(len as u64)); + + group.bench_with_input( + criterion::BenchmarkId::new("iterate", name), + &len, + |b, _| { + b.iter(|| black_box(iterate.parse_peek(black_box(sample)).unwrap())); + }, + ); + group.bench_with_input( + criterion::BenchmarkId::new("next_token", name), + &len, + |b, _| { + b.iter(|| black_box(next_token.parse_peek(black_box(sample)).unwrap())); + }, + ); + group.bench_with_input( + criterion::BenchmarkId::new("opt(one_of)", name), + &len, + |b, _| { + b.iter(|| black_box(opt_one_of.parse_peek(black_box(sample)).unwrap())); + }, + ); + group.bench_with_input( + criterion::BenchmarkId::new("take_while", name), + &len, + |b, _| { + b.iter(|| black_box(take_while.parse_peek(black_box(sample)).unwrap())); + }, + ); + group.bench_with_input(criterion::BenchmarkId::new("repeat", name), &len, |b, _| { + b.iter(|| black_box(repeat.parse_peek(black_box(sample)).unwrap())); + }); + } + group.finish(); +} + +fn iterate(input: &mut &[u8]) -> PResult<usize> { + let mut count = 0; + for byte in input.iter() { + if byte.is_dec_digit() { + count += 1; + } + } + input.finish(); + Ok(count) +} + +fn next_token(input: &mut &[u8]) -> PResult<usize> { + let mut count = 0; + while let Some(byte) = input.next_token() { + if byte.is_dec_digit() { + count += 1; + } + } + Ok(count) +} + +fn opt_one_of(input: &mut &[u8]) -> PResult<usize> { + let mut count = 0; + while !input.is_empty() { + while opt(one_of(AsChar::is_dec_digit)) + .parse_next(input)? + .is_some() + { + count += 1; + } + while opt(one_of(|b: u8| !b.is_dec_digit())) + .parse_next(input)? + .is_some() + {} + } + Ok(count) +} + +fn take_while(input: &mut &[u8]) -> PResult<usize> { + let mut count = 0; + while !input.is_empty() { + count += winnow::token::take_while(0.., AsChar::is_dec_digit) + .parse_next(input)? + .len(); + let _ = winnow::token::take_while(0.., |b: u8| !b.is_dec_digit()).parse_next(input)?; + } + Ok(count) +} + +fn repeat(input: &mut &[u8]) -> PResult<usize> { + let mut count = 0; + while !input.is_empty() { + 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)?; + } + Ok(count) +} + +const CONTIGUOUS: &str = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; +const INTERLEAVED: &str = "0123456789abc0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab"; +const CANADA: &str = include_str!("../third_party/nativejson-benchmark/data/canada.json"); + +criterion::criterion_group!(benches, iter); +criterion::criterion_main!(benches); diff --git a/vendor/winnow/benches/number.rs b/vendor/winnow/benches/number.rs new file mode 100644 index 0000000..d35d65c --- /dev/null +++ b/vendor/winnow/benches/number.rs @@ -0,0 +1,70 @@ +#[macro_use] +extern crate criterion; + +use criterion::Criterion; + +use winnow::ascii::float; +use winnow::binary::be_u64; +use winnow::error::ErrMode; +use winnow::error::ErrorKind; +use winnow::error::InputError; +use winnow::error::ParserError; +use winnow::prelude::*; +use winnow::stream::ParseSlice; + +type Stream<'i> = &'i [u8]; + +fn parser(i: &mut Stream<'_>) -> PResult<u64> { + be_u64.parse_next(i) +} + +fn number(c: &mut Criterion) { + let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + + parser + .parse_peek(&data[..]) + .expect("should parse correctly"); + c.bench_function("number", move |b| { + b.iter(|| parser.parse_peek(&data[..]).unwrap()); + }); +} + +fn float_bytes(c: &mut Criterion) { + println!( + "float_bytes result: {:?}", + float::<_, f64, InputError<_>>.parse_peek(&b"-1.234E-12"[..]) + ); + c.bench_function("float bytes", |b| { + b.iter(|| float::<_, f64, InputError<_>>.parse_peek(&b"-1.234E-12"[..])); + }); +} + +fn float_str(c: &mut Criterion) { + println!( + "float_str result: {:?}", + float::<_, f64, InputError<_>>.parse_peek("-1.234E-12") + ); + c.bench_function("float str", |b| { + b.iter(|| float::<_, f64, InputError<_>>.parse_peek("-1.234E-12")); + }); +} + +fn std_float(input: &mut &[u8]) -> PResult<f64> { + match input.parse_slice() { + Some(n) => Ok(n), + None => Err(ErrMode::from_error_kind(input, ErrorKind::Slice)), + } +} + +fn std_float_bytes(c: &mut Criterion) { + println!( + "std_float_bytes result: {:?}", + std_float.parse_peek(&b"-1.234E-12"[..]) + ); + c.bench_function("std_float bytes", |b| { + b.iter(|| std_float.parse_peek(&b"-1.234E-12"[..])); + }); +} + +criterion_group!(benches, number, float_bytes, std_float_bytes, float_str); +criterion_main!(benches); diff --git a/vendor/winnow/debian/patches/drop-debug-feature.patch b/vendor/winnow/debian/patches/drop-debug-feature.patch new file mode 100644 index 0000000..1e1c75a --- /dev/null +++ b/vendor/winnow/debian/patches/drop-debug-feature.patch @@ -0,0 +1,45 @@ +Index: winnow/Cargo.toml +=================================================================== +--- winnow.orig/Cargo.toml ++++ winnow/Cargo.toml +@@ -168,27 +168,11 @@ path = "examples/json/bench.rs" + harness = false + required-features = ["std"] + +-[dependencies.anstream] +-version = "0.3.2" +-optional = true +- +-[dependencies.anstyle] +-version = "1.0.1" +-optional = true +- +-[dependencies.is-terminal] +-version = "0.4.9" +-optional = true +- + [dependencies.memchr] + version = "2.5" + optional = true + default-features = false + +-[dependencies.terminal_size] +-version = "0.2.6" +-optional = true +- + [dev-dependencies.circular] + version = "0.3.0" + +@@ -216,12 +200,6 @@ version = "0.2.0" + + [features] + alloc = [] +-debug = [ +- "dep:anstream", +- "dep:anstyle", +- "dep:is-terminal", +- "dep:terminal_size", +-] + default = ["std"] + simd = ["dep:memchr"] + std = [ diff --git a/vendor/winnow/debian/patches/series b/vendor/winnow/debian/patches/series new file mode 100644 index 0000000..38c1d3d --- /dev/null +++ b/vendor/winnow/debian/patches/series @@ -0,0 +1 @@ +drop-debug-feature.patch diff --git a/vendor/winnow/examples/arithmetic/bench.rs b/vendor/winnow/examples/arithmetic/bench.rs new file mode 100644 index 0000000..692ac22 --- /dev/null +++ b/vendor/winnow/examples/arithmetic/bench.rs @@ -0,0 +1,33 @@ +mod parser; +mod parser_ast; +mod parser_lexer; + +use winnow::prelude::*; + +#[allow(clippy::eq_op, clippy::erasing_op)] +fn arithmetic(c: &mut criterion::Criterion) { + let data = " 2*2 / ( 5 - 1) + 3 / 4 * (2 - 7 + 567 *12 /2) + 3*(1+2*( 45 /2))"; + let expected = 2 * 2 / (5 - 1) + 3 * (1 + 2 * (45 / 2)); + + assert_eq!(parser::expr.parse(data), Ok(expected)); + assert_eq!( + parser_ast::expr.parse(data).map(|ast| ast.eval()), + Ok(expected) + ); + assert_eq!( + parser_lexer::expr2.parse(data).map(|ast| ast.eval()), + Ok(expected) + ); + c.bench_function("direct", |b| { + b.iter(|| parser::expr.parse(data).unwrap()); + }); + c.bench_function("ast", |b| { + b.iter(|| parser_ast::expr.parse(data).unwrap().eval()); + }); + c.bench_function("lexer", |b| { + b.iter(|| parser_lexer::expr2.parse_peek(data).unwrap()); + }); +} + +criterion::criterion_group!(benches, arithmetic); +criterion::criterion_main!(benches); diff --git a/vendor/winnow/examples/arithmetic/main.rs b/vendor/winnow/examples/arithmetic/main.rs new file mode 100644 index 0000000..e46cf2f --- /dev/null +++ b/vendor/winnow/examples/arithmetic/main.rs @@ -0,0 +1,86 @@ +use winnow::prelude::*; + +mod parser; +mod parser_ast; +mod parser_lexer; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + + let input = args.input.as_deref().unwrap_or("1 + 1"); + if let Err(err) = calc(input, args.implementation) { + println!("FAILED"); + println!("{}", err); + } + + Ok(()) +} + +fn calc( + input: &str, + imp: Impl, +) -> Result<(), winnow::error::ParseError<&str, winnow::error::ContextError>> { + println!("{} =", input); + match imp { + Impl::Eval => { + let result = parser::expr.parse(input)?; + println!(" {}", result); + } + Impl::Ast => { + let result = parser_ast::expr.parse(input)?; + println!(" {:#?}={}", result, result.eval()); + } + Impl::Lexer => { + let tokens = parser_lexer::lex.parse(input)?; + println!(" {:#?}", tokens); + let result = parser_lexer::expr.parse(tokens.as_slice()).unwrap(); + println!(" {:#?}={}", result, result.eval()); + } + } + Ok(()) +} + +#[derive(Default)] +struct Args { + input: Option<String>, + implementation: Impl, +} + +enum Impl { + Eval, + Ast, + Lexer, +} + +impl Default for Impl { + fn default() -> Self { + Self::Eval + } +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Long("impl") => { + res.implementation = args.value()?.parse_with(|s| match s { + "eval" => Ok(Impl::Eval), + "ast" => Ok(Impl::Ast), + "lexer" => Ok(Impl::Lexer), + _ => Err("expected `eval`, `ast`"), + })?; + } + Value(input) => { + res.input = Some(input.string()?); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} diff --git a/vendor/winnow/examples/arithmetic/parser.rs b/vendor/winnow/examples/arithmetic/parser.rs new file mode 100644 index 0000000..b776917 --- /dev/null +++ b/vendor/winnow/examples/arithmetic/parser.rs @@ -0,0 +1,137 @@ +use std::str::FromStr; + +use winnow::prelude::*; +use winnow::{ + ascii::{digit1 as digits, multispace0 as multispaces}, + combinator::alt, + combinator::delimited, + combinator::fold_repeat, + token::one_of, +}; + +// Parser definition + +pub fn expr(i: &mut &str) -> PResult<i64> { + let init = term.parse_next(i)?; + + fold_repeat( + 0.., + (one_of(['+', '-']), term), + move || init, + |acc, (op, val): (char, i64)| { + if op == '+' { + acc + val + } else { + acc - val + } + }, + ) + .parse_next(i) +} + +// We read an initial factor and for each time we find +// a * or / operator followed by another factor, we do +// the math by folding everything +fn term(i: &mut &str) -> PResult<i64> { + let init = factor.parse_next(i)?; + + fold_repeat( + 0.., + (one_of(['*', '/']), factor), + move || init, + |acc, (op, val): (char, i64)| { + if op == '*' { + acc * val + } else { + acc / val + } + }, + ) + .parse_next(i) +} + +// We transform an integer string into a i64, ignoring surrounding whitespaces +// We look for a digit suite, and try to convert it. +// If either str::from_utf8 or FromStr::from_str fail, +// we fallback to the parens parser defined above +fn factor(i: &mut &str) -> PResult<i64> { + delimited( + multispaces, + alt((digits.try_map(FromStr::from_str), parens)), + multispaces, + ) + .parse_next(i) +} + +// We parse any expr surrounded by parens, ignoring all whitespaces around those +fn parens(i: &mut &str) -> PResult<i64> { + delimited('(', expr, ')').parse_next(i) +} + +#[test] +fn factor_test() { + let input = "3"; + let expected = Ok(("", 3)); + assert_eq!(factor.parse_peek(input), expected); + + let input = " 12"; + let expected = Ok(("", 12)); + assert_eq!(factor.parse_peek(input), expected); + + let input = "537 "; + let expected = Ok(("", 537)); + assert_eq!(factor.parse_peek(input), expected); + + let input = " 24 "; + let expected = Ok(("", 24)); + assert_eq!(factor.parse_peek(input), expected); +} + +#[test] +fn term_test() { + let input = " 12 *2 / 3"; + let expected = Ok(("", 8)); + assert_eq!(term.parse_peek(input), expected); + + let input = " 12 *2 / 3"; + let expected = Ok(("", 8)); + assert_eq!(term.parse_peek(input), expected); + + let input = " 2* 3 *2 *2 / 3"; + let expected = Ok(("", 8)); + assert_eq!(term.parse_peek(input), expected); + + let input = " 48 / 3/2"; + let expected = Ok(("", 8)); + assert_eq!(term.parse_peek(input), expected); +} + +#[test] +fn expr_test() { + let input = " 1 + 2 "; + let expected = Ok(("", 3)); + assert_eq!(expr.parse_peek(input), expected); + + let input = " 12 + 6 - 4+ 3"; + let expected = Ok(("", 17)); + assert_eq!(expr.parse_peek(input), expected); + + let input = " 1 + 2*3 + 4"; + let expected = Ok(("", 11)); + assert_eq!(expr.parse_peek(input), expected); +} + +#[test] +fn parens_test() { + let input = " ( 2 )"; + let expected = Ok(("", 2)); + assert_eq!(expr.parse_peek(input), expected); + + let input = " 2* ( 3 + 4 ) "; + let expected = Ok(("", 14)); + assert_eq!(expr.parse_peek(input), expected); + + let input = " 2*2 / ( 5 - 1) + 3"; + let expected = Ok(("", 4)); + assert_eq!(expr.parse_peek(input), expected); +} diff --git a/vendor/winnow/examples/arithmetic/parser_ast.rs b/vendor/winnow/examples/arithmetic/parser_ast.rs new file mode 100644 index 0000000..0ca1534 --- /dev/null +++ b/vendor/winnow/examples/arithmetic/parser_ast.rs @@ -0,0 +1,184 @@ +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; + +use std::str::FromStr; + +use winnow::prelude::*; +use winnow::{ + ascii::{digit1 as digits, multispace0 as multispaces}, + combinator::alt, + combinator::delimited, + combinator::fold_repeat, + token::one_of, +}; + +#[derive(Debug, Clone)] +pub enum Expr { + Value(i64), + Add(Box<Expr>, Box<Expr>), + Sub(Box<Expr>, Box<Expr>), + Mul(Box<Expr>, Box<Expr>), + Div(Box<Expr>, Box<Expr>), + Paren(Box<Expr>), +} + +impl Expr { + pub fn eval(&self) -> i64 { + match self { + Self::Value(v) => *v, + Self::Add(lhs, rhs) => lhs.eval() + rhs.eval(), + Self::Sub(lhs, rhs) => lhs.eval() - rhs.eval(), + Self::Mul(lhs, rhs) => lhs.eval() * rhs.eval(), + Self::Div(lhs, rhs) => lhs.eval() / rhs.eval(), + Self::Paren(expr) => expr.eval(), + } + } +} + +impl Display for Expr { + 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), + } + } +} + +pub fn expr(i: &mut &str) -> PResult<Expr> { + let init = term.parse_next(i)?; + + fold_repeat( + 0.., + (one_of(['+', '-']), term), + move || init.clone(), + |acc, (op, val): (char, Expr)| { + if op == '+' { + Expr::Add(Box::new(acc), Box::new(val)) + } else { + Expr::Sub(Box::new(acc), Box::new(val)) + } + }, + ) + .parse_next(i) +} + +fn term(i: &mut &str) -> PResult<Expr> { + let init = factor.parse_next(i)?; + + fold_repeat( + 0.., + (one_of(['*', '/']), factor), + move || init.clone(), + |acc, (op, val): (char, Expr)| { + if op == '*' { + Expr::Mul(Box::new(acc), Box::new(val)) + } else { + Expr::Div(Box::new(acc), Box::new(val)) + } + }, + ) + .parse_next(i) +} + +fn factor(i: &mut &str) -> PResult<Expr> { + delimited( + multispaces, + alt((digits.try_map(FromStr::from_str).map(Expr::Value), parens)), + multispaces, + ) + .parse_next(i) +} + +fn parens(i: &mut &str) -> PResult<Expr> { + delimited("(", expr, ")") + .map(|e| Expr::Paren(Box::new(e))) + .parse_next(i) +} + +#[test] +fn factor_test() { + let input = "3"; + let expected = Ok(("", String::from("Value(3)"))); + assert_eq!(factor.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 12"; + let expected = Ok(("", String::from("Value(12)"))); + assert_eq!(factor.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = "537 "; + let expected = Ok(("", String::from("Value(537)"))); + assert_eq!(factor.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 24 "; + let expected = Ok(("", String::from("Value(24)"))); + assert_eq!(factor.map(|e| format!("{e:?}")).parse_peek(input), expected); +} + +#[test] +fn term_test() { + let input = " 12 *2 / 3"; + let expected = Ok(("", String::from("Div(Mul(Value(12), Value(2)), Value(3))"))); + assert_eq!(term.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 12 *2 / 3"; + let expected = Ok(("", String::from("Div(Mul(Value(12), Value(2)), Value(3))"))); + assert_eq!(term.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 2* 3 *2 *2 / 3"; + let expected = Ok(( + "", + String::from("Div(Mul(Mul(Mul(Value(2), Value(3)), Value(2)), Value(2)), Value(3))"), + )); + assert_eq!(term.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 48 / 3/2"; + let expected = Ok(("", String::from("Div(Div(Value(48), Value(3)), Value(2))"))); + assert_eq!(term.map(|e| format!("{e:?}")).parse_peek(input), expected); +} + +#[test] +fn expr_test() { + let input = " 1 + 2 "; + let expected = Ok(("", String::from("Add(Value(1), Value(2))"))); + assert_eq!(expr.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 12 + 6 - 4+ 3"; + let expected = Ok(( + "", + String::from("Add(Sub(Add(Value(12), Value(6)), Value(4)), Value(3))"), + )); + assert_eq!(expr.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 1 + 2*3 + 4"; + let expected = Ok(( + "", + String::from("Add(Add(Value(1), Mul(Value(2), Value(3))), Value(4))"), + )); + assert_eq!(expr.map(|e| format!("{e:?}")).parse_peek(input), expected); +} + +#[test] +fn parens_test() { + let input = " ( 2 )"; + let expected = Ok(("", String::from("Paren(Value(2))"))); + assert_eq!(expr.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 2* ( 3 + 4 ) "; + let expected = Ok(( + "", + String::from("Mul(Value(2), Paren(Add(Value(3), Value(4))))"), + )); + assert_eq!(expr.map(|e| format!("{e:?}")).parse_peek(input), expected); + + let input = " 2*2 / ( 5 - 1) + 3"; + let expected = Ok(( + "", + String::from("Add(Div(Mul(Value(2), Value(2)), Paren(Sub(Value(5), Value(1)))), Value(3))"), + )); + assert_eq!(expr.map(|e| format!("{e:?}")).parse_peek(input), expected); +} diff --git a/vendor/winnow/examples/arithmetic/parser_lexer.rs b/vendor/winnow/examples/arithmetic/parser_lexer.rs new file mode 100644 index 0000000..f49566d --- /dev/null +++ b/vendor/winnow/examples/arithmetic/parser_lexer.rs @@ -0,0 +1,297 @@ +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; + +use std::str::FromStr; + +use winnow::prelude::*; +use winnow::{ + ascii::{digit1 as digits, multispace0 as multispaces}, + combinator::alt, + combinator::dispatch, + combinator::fail, + combinator::fold_repeat, + combinator::peek, + combinator::repeat, + combinator::{delimited, preceded, terminated}, + token::any, + token::one_of, +}; + +#[derive(Debug, Clone)] +pub enum Expr { + Value(i64), + Add(Box<Expr>, Box<Expr>), + Sub(Box<Expr>, Box<Expr>), + Mul(Box<Expr>, Box<Expr>), + Div(Box<Expr>, Box<Expr>), + Paren(Box<Expr>), +} + +impl Expr { + pub fn eval(&self) -> i64 { + match self { + Self::Value(v) => *v, + Self::Add(lhs, rhs) => lhs.eval() + rhs.eval(), + Self::Sub(lhs, rhs) => lhs.eval() - rhs.eval(), + Self::Mul(lhs, rhs) => lhs.eval() * rhs.eval(), + Self::Div(lhs, rhs) => lhs.eval() / rhs.eval(), + Self::Paren(expr) => expr.eval(), + } + } +} + +impl Display for Expr { + 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), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Token { + Value(i64), + Oper(Oper), + OpenParen, + CloseParen, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Oper { + Add, + Sub, + Mul, + Div, +} + +impl winnow::stream::ContainsToken<Token> for Token { + #[inline(always)] + fn contains_token(&self, token: Token) -> bool { + *self == token + } +} + +impl winnow::stream::ContainsToken<Token> for &'_ [Token] { + #[inline] + fn contains_token(&self, token: Token) -> bool { + self.iter().any(|t| *t == token) + } +} + +impl<const LEN: usize> winnow::stream::ContainsToken<Token> for &'_ [Token; LEN] { + #[inline] + fn contains_token(&self, token: Token) -> bool { + self.iter().any(|t| *t == token) + } +} + +impl<const LEN: usize> winnow::stream::ContainsToken<Token> for [Token; LEN] { + #[inline] + fn contains_token(&self, token: Token) -> bool { + self.iter().any(|t| *t == token) + } +} + +#[allow(dead_code)] +pub 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>> { + preceded(multispaces, repeat(1.., terminated(token, multispaces))).parse_next(i) +} + +fn token(i: &mut &str) -> PResult<Token> { + dispatch! {peek(any); + '0'..='9' => digits.try_map(FromStr::from_str).map(Token::Value), + '(' => '('.value(Token::OpenParen), + ')' => ')'.value(Token::CloseParen), + '+' => '+'.value(Token::Oper(Oper::Add)), + '-' => '-'.value(Token::Oper(Oper::Sub)), + '*' => '*'.value(Token::Oper(Oper::Mul)), + '/' => '/'.value(Token::Oper(Oper::Div)), + _ => fail, + } + .parse_next(i) +} + +pub fn expr(i: &mut &[Token]) -> PResult<Expr> { + let init = term.parse_next(i)?; + + fold_repeat( + 0.., + ( + one_of([Token::Oper(Oper::Add), Token::Oper(Oper::Sub)]), + term, + ), + move || init.clone(), + |acc, (op, val): (Token, Expr)| { + if op == Token::Oper(Oper::Add) { + Expr::Add(Box::new(acc), Box::new(val)) + } else { + Expr::Sub(Box::new(acc), Box::new(val)) + } + }, + ) + .parse_next(i) +} + +fn term(i: &mut &[Token]) -> PResult<Expr> { + let init = factor.parse_next(i)?; + + fold_repeat( + 0.., + ( + one_of([Token::Oper(Oper::Mul), Token::Oper(Oper::Div)]), + factor, + ), + move || init.clone(), + |acc, (op, val): (Token, Expr)| { + if op == Token::Oper(Oper::Mul) { + Expr::Mul(Box::new(acc), Box::new(val)) + } else { + Expr::Div(Box::new(acc), Box::new(val)) + } + }, + ) + .parse_next(i) +} + +fn factor(i: &mut &[Token]) -> PResult<Expr> { + alt(( + one_of(|t| matches!(t, Token::Value(_))).map(|t| match t { + Token::Value(v) => Expr::Value(v), + _ => unreachable!(), + }), + parens, + )) + .parse_next(i) +} + +fn parens(i: &mut &[Token]) -> PResult<Expr> { + delimited(one_of(Token::OpenParen), expr, one_of(Token::CloseParen)) + .map(|e| Expr::Paren(Box::new(e))) + .parse_next(i) +} + +#[test] +fn lex_test() { + let input = "3"; + let expected = Ok(String::from(r#"("", [Value(3)])"#)); + assert_eq!(lex.parse_peek(input).map(|e| format!("{e:?}")), expected); + + let input = " 24 "; + let expected = Ok(String::from(r#"("", [Value(24)])"#)); + assert_eq!(lex.parse_peek(input).map(|e| format!("{e:?}")), expected); + + let input = " 12 *2 / 3"; + let expected = Ok(String::from( + r#"("", [Value(12), Oper(Mul), Value(2), Oper(Div), Value(3)])"#, + )); + assert_eq!(lex.parse_peek(input).map(|e| format!("{e:?}")), expected); + + let input = " 2*2 / ( 5 - 1) + 3"; + let expected = Ok(String::from( + r#"("", [Value(2), Oper(Mul), Value(2), Oper(Div), OpenParen, Value(5), Oper(Sub), Value(1), CloseParen, Oper(Add), Value(3)])"#, + )); + assert_eq!(lex.parse_peek(input).map(|e| format!("{e:?}")), expected); +} + +#[test] +fn factor_test() { + let input = "3"; + let expected = Ok(String::from("Value(3)")); + let input = lex.parse(input).unwrap(); + assert_eq!(factor.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 12"; + let expected = Ok(String::from("Value(12)")); + let input = lex.parse(input).unwrap(); + assert_eq!(factor.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = "537 "; + let expected = Ok(String::from("Value(537)")); + let input = lex.parse(input).unwrap(); + assert_eq!(factor.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 24 "; + let expected = Ok(String::from("Value(24)")); + let input = lex.parse(input).unwrap(); + assert_eq!(factor.map(|e| format!("{e:?}")).parse(&input), expected); +} + +#[test] +fn term_test() { + let input = " 12 *2 / 3"; + let expected = Ok(String::from("Div(Mul(Value(12), Value(2)), Value(3))")); + let input = lex.parse(input).unwrap(); + assert_eq!(term.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 12 *2 / 3"; + let expected = Ok(String::from("Div(Mul(Value(12), Value(2)), Value(3))")); + let input = lex.parse(input).unwrap(); + assert_eq!(term.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 2* 3 *2 *2 / 3"; + let expected = Ok(String::from( + "Div(Mul(Mul(Mul(Value(2), Value(3)), Value(2)), Value(2)), Value(3))", + )); + let input = lex.parse(input).unwrap(); + assert_eq!(term.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 48 / 3/2"; + let expected = Ok(String::from("Div(Div(Value(48), Value(3)), Value(2))")); + let input = lex.parse(input).unwrap(); + assert_eq!(term.map(|e| format!("{e:?}")).parse(&input), expected); +} + +#[test] +fn expr_test() { + let input = " 1 + 2 "; + let expected = Ok(String::from("Add(Value(1), Value(2))")); + let input = lex.parse(input).unwrap(); + assert_eq!(expr.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 12 + 6 - 4+ 3"; + let expected = Ok(String::from( + "Add(Sub(Add(Value(12), Value(6)), Value(4)), Value(3))", + )); + let input = lex.parse(input).unwrap(); + assert_eq!(expr.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 1 + 2*3 + 4"; + let expected = Ok(String::from( + "Add(Add(Value(1), Mul(Value(2), Value(3))), Value(4))", + )); + let input = lex.parse(input).unwrap(); + assert_eq!(expr.map(|e| format!("{e:?}")).parse(&input), expected); +} + +#[test] +fn parens_test() { + let input = " ( 2 )"; + let expected = Ok(String::from("Paren(Value(2))")); + let input = lex.parse(input).unwrap(); + assert_eq!(expr.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 2* ( 3 + 4 ) "; + let expected = Ok(String::from( + "Mul(Value(2), Paren(Add(Value(3), Value(4))))", + )); + let input = lex.parse(input).unwrap(); + assert_eq!(expr.map(|e| format!("{e:?}")).parse(&input), expected); + + let input = " 2*2 / ( 5 - 1) + 3"; + let expected = Ok(String::from( + "Add(Div(Mul(Value(2), Value(2)), Paren(Sub(Value(5), Value(1)))), Value(3))", + )); + let input = lex.parse(input).unwrap(); + assert_eq!(expr.map(|e| format!("{e:?}")).parse(&input), expected); +} diff --git a/vendor/winnow/examples/css/main.rs b/vendor/winnow/examples/css/main.rs new file mode 100644 index 0000000..cf52ada --- /dev/null +++ b/vendor/winnow/examples/css/main.rs @@ -0,0 +1,62 @@ +use winnow::prelude::*; + +mod parser; + +use parser::hex_color; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + + let input = args.input.as_deref().unwrap_or("#AAAAAA"); + + println!("{} =", input); + match hex_color.parse(input) { + Ok(result) => { + println!(" {:?}", result); + } + Err(err) => { + println!(" {}", err); + } + } + + Ok(()) +} + +#[derive(Default)] +struct Args { + input: Option<String>, +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Value(input) => { + res.input = Some(input.string()?); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} + +#[test] +fn parse_color() { + assert_eq!( + hex_color.parse_peek("#2F14DF"), + Ok(( + "", + parser::Color { + red: 47, + green: 20, + blue: 223, + } + )) + ); +} diff --git a/vendor/winnow/examples/css/parser.rs b/vendor/winnow/examples/css/parser.rs new file mode 100644 index 0000000..d31ed0b --- /dev/null +++ b/vendor/winnow/examples/css/parser.rs @@ -0,0 +1,31 @@ +use winnow::prelude::*; +use winnow::token::take_while; + +#[derive(Debug, Eq, PartialEq)] +pub struct Color { + pub red: u8, + pub green: u8, + pub blue: u8, +} + +impl std::str::FromStr for Color { + // The error must be owned + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + hex_color.parse(s).map_err(|e| e.to_string()) + } +} + +pub fn hex_color(input: &mut &str) -> PResult<Color> { + let _ = "#".parse_next(input)?; + let (red, green, blue) = (hex_primary, hex_primary, hex_primary).parse_next(input)?; + + Ok(Color { red, green, blue }) +} + +fn hex_primary(input: &mut &str) -> PResult<u8> { + take_while(2, |c: char| c.is_ascii_hexdigit()) + .try_map(|input| u8::from_str_radix(input, 16)) + .parse_next(input) +} diff --git a/vendor/winnow/examples/custom_error.rs b/vendor/winnow/examples/custom_error.rs new file mode 100644 index 0000000..998e5ad --- /dev/null +++ b/vendor/winnow/examples/custom_error.rs @@ -0,0 +1,40 @@ +use winnow::error::ErrMode; +use winnow::error::ErrorKind; +use winnow::error::ParserError; +use winnow::prelude::*; + +#[derive(Debug, PartialEq, Eq)] +pub enum CustomError<I> { + MyError, + Nom(I, ErrorKind), +} + +impl<I: Clone> ParserError<I> for CustomError<I> { + fn from_error_kind(input: &I, kind: ErrorKind) -> Self { + CustomError::Nom(input.clone(), kind) + } + + fn append(self, _: &I, _: ErrorKind) -> Self { + self + } +} + +pub fn parse<'s>(_input: &mut &'s str) -> PResult<&'s str, CustomError<&'s str>> { + Err(ErrMode::Backtrack(CustomError::MyError)) +} + +fn main() {} + +#[cfg(test)] +mod tests { + use super::*; + + #[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), + } + } +} diff --git a/vendor/winnow/examples/http/bench.rs b/vendor/winnow/examples/http/bench.rs new file mode 100644 index 0000000..a27a119 --- /dev/null +++ b/vendor/winnow/examples/http/bench.rs @@ -0,0 +1,36 @@ +mod parser; +mod parser_streaming; + +fn one_test(c: &mut criterion::Criterion) { + let data = &b"GET / HTTP/1.1 +Host: www.reddit.com +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip, deflate +Connection: keep-alive + +"[..]; + + let mut http_group = c.benchmark_group("http"); + http_group.throughput(criterion::Throughput::Bytes(data.len() as u64)); + http_group.bench_with_input( + criterion::BenchmarkId::new("complete", data.len()), + data, + |b, data| { + b.iter(|| parser::parse(data).unwrap()); + }, + ); + http_group.bench_with_input( + criterion::BenchmarkId::new("streaming", data.len()), + data, + |b, data| { + b.iter(|| parser_streaming::parse(data).unwrap()); + }, + ); + + http_group.finish(); +} + +criterion::criterion_group!(http, one_test); +criterion::criterion_main!(http); diff --git a/vendor/winnow/examples/http/main.rs b/vendor/winnow/examples/http/main.rs new file mode 100644 index 0000000..b0e480f --- /dev/null +++ b/vendor/winnow/examples/http/main.rs @@ -0,0 +1,47 @@ +mod parser; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + + let input = args.input.as_deref().unwrap_or( + "GET / HTTP/1.1 +Host: www.reddit.com +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip, deflate +Connection: keep-alive + +", + ); + + if let Some(result) = parser::parse(input.as_bytes()) { + println!(" {:#?}", result); + } + + Ok(()) +} + +#[derive(Default)] +struct Args { + input: Option<String>, +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Value(input) => { + res.input = Some(input.string()?); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} diff --git a/vendor/winnow/examples/http/parser.rs b/vendor/winnow/examples/http/parser.rs new file mode 100644 index 0000000..3a04754 --- /dev/null +++ b/vendor/winnow/examples/http/parser.rs @@ -0,0 +1,138 @@ +use winnow::prelude::*; +use winnow::{ascii::line_ending, combinator::repeat, token::take_while}; + +pub type Stream<'i> = &'i [u8]; + +#[rustfmt::skip] +#[derive(Debug)] +#[allow(dead_code)] +pub struct Request<'a> { + method: &'a [u8], + uri: &'a [u8], + version: &'a [u8], +} + +#[derive(Debug)] +#[allow(dead_code)] +pub struct Header<'a> { + name: &'a [u8], + value: Vec<&'a [u8]>, +} + +pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> { + let mut buf = data; + let mut v = Vec::new(); + loop { + match request(&mut buf) { + Ok(r) => { + v.push(r); + + if buf.is_empty() { + //println!("{}", i); + break; + } + } + Err(e) => { + println!("error: {:?}", e); + return None; + } + } + } + + Some(v) +} + +fn request<'s>(input: &mut Stream<'s>) -> PResult<(Request<'s>, Vec<Header<'s>>)> { + let req = request_line(input)?; + let h = repeat(1.., message_header).parse_next(input)?; + let _ = line_ending.parse_next(input)?; + + Ok((req, h)) +} + +fn request_line<'s>(input: &mut Stream<'s>) -> PResult<Request<'s>> { + let method = take_while(1.., is_token).parse_next(input)?; + let _ = take_while(1.., is_space).parse_next(input)?; + let uri = take_while(1.., is_not_space).parse_next(input)?; + let _ = take_while(1.., is_space).parse_next(input)?; + let version = http_version(input)?; + let _ = line_ending.parse_next(input)?; + + Ok(Request { + method, + uri, + version, + }) +} + +fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { + let _ = "HTTP/".parse_next(input)?; + let version = take_while(1.., is_version).parse_next(input)?; + + Ok(version) +} + +fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { + let _ = take_while(1.., is_horizontal_space).parse_next(input)?; + let data = take_while(1.., not_line_ending).parse_next(input)?; + let _ = line_ending.parse_next(input)?; + + Ok(data) +} + +fn message_header<'s>(input: &mut Stream<'s>) -> PResult<Header<'s>> { + let name = take_while(1.., is_token).parse_next(input)?; + let _ = ':'.parse_next(input)?; + let value = repeat(1.., message_header_value).parse_next(input)?; + + Ok(Header { name, value }) +} + +#[rustfmt::skip] +#[allow(clippy::match_same_arms)] +#[allow(clippy::match_like_matches_macro)] +fn is_token(c: u8) -> bool { + match c { + 128..=255 => false, + 0..=31 => false, + b'(' => false, + b')' => false, + b'<' => false, + b'>' => false, + b'@' => false, + b',' => false, + b';' => false, + b':' => false, + b'\\' => false, + b'"' => false, + b'/' => false, + b'[' => false, + b']' => false, + b'?' => false, + b'=' => false, + b'{' => false, + b'}' => false, + b' ' => false, + _ => true, + } +} + +fn is_version(c: u8) -> bool { + c.is_ascii_digit() || c == b'.' +} + +fn not_line_ending(c: u8) -> bool { + c != b'\r' && c != b'\n' +} + +fn is_space(c: u8) -> bool { + c == b' ' +} + +fn is_not_space(c: u8) -> bool { + c != b' ' +} + +fn is_horizontal_space(c: u8) -> bool { + c == b' ' || c == b'\t' +} diff --git a/vendor/winnow/examples/http/parser_streaming.rs b/vendor/winnow/examples/http/parser_streaming.rs new file mode 100644 index 0000000..1f850e8 --- /dev/null +++ b/vendor/winnow/examples/http/parser_streaming.rs @@ -0,0 +1,139 @@ +use winnow::{ + ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while, +}; + +pub type Stream<'i> = Partial<&'i [u8]>; + +#[rustfmt::skip] +#[derive(Debug)] +#[allow(dead_code)] +pub struct Request<'a> { + method: &'a [u8], + uri: &'a [u8], + version: &'a [u8], +} + +#[derive(Debug)] +#[allow(dead_code)] +pub struct Header<'a> { + name: &'a [u8], + value: Vec<&'a [u8]>, +} + +pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> { + let mut buf = Partial::new(data); + let mut v = Vec::new(); + loop { + match request(&mut buf) { + Ok(r) => { + v.push(r); + + if buf.is_empty() { + //println!("{}", i); + break; + } + } + Err(e) => { + println!("error: {:?}", e); + return None; + } + } + } + + Some(v) +} + +fn request<'s>(input: &mut Stream<'s>) -> PResult<(Request<'s>, Vec<Header<'s>>)> { + let req = request_line(input)?; + let h = repeat(1.., message_header).parse_next(input)?; + let _ = line_ending.parse_next(input)?; + + Ok((req, h)) +} + +fn request_line<'s>(input: &mut Stream<'s>) -> PResult<Request<'s>> { + let method = take_while(1.., is_token).parse_next(input)?; + let _ = take_while(1.., is_space).parse_next(input)?; + let uri = take_while(1.., is_not_space).parse_next(input)?; + let _ = take_while(1.., is_space).parse_next(input)?; + let version = http_version(input)?; + let _ = line_ending.parse_next(input)?; + + Ok(Request { + method, + uri, + version, + }) +} + +fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { + let _ = "HTTP/".parse_next(input)?; + let version = take_while(1.., is_version).parse_next(input)?; + + Ok(version) +} + +fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { + let _ = take_while(1.., is_horizontal_space).parse_next(input)?; + let data = take_while(1.., not_line_ending).parse_next(input)?; + let _ = line_ending.parse_next(input)?; + + Ok(data) +} + +fn message_header<'s>(input: &mut Stream<'s>) -> PResult<Header<'s>> { + let name = take_while(1.., is_token).parse_next(input)?; + let _ = ':'.parse_next(input)?; + let value = repeat(1.., message_header_value).parse_next(input)?; + + Ok(Header { name, value }) +} + +#[rustfmt::skip] +#[allow(clippy::match_same_arms)] +#[allow(clippy::match_like_matches_macro)] +fn is_token(c: u8) -> bool { + match c { + 128..=255 => false, + 0..=31 => false, + b'(' => false, + b')' => false, + b'<' => false, + b'>' => false, + b'@' => false, + b',' => false, + b';' => false, + b':' => false, + b'\\' => false, + b'"' => false, + b'/' => false, + b'[' => false, + b']' => false, + b'?' => false, + b'=' => false, + b'{' => false, + b'}' => false, + b' ' => false, + _ => true, + } +} + +fn is_version(c: u8) -> bool { + c.is_ascii_digit() || c == b'.' +} + +fn not_line_ending(c: u8) -> bool { + c != b'\r' && c != b'\n' +} + +fn is_space(c: u8) -> bool { + c == b' ' +} + +fn is_not_space(c: u8) -> bool { + c != b' ' +} + +fn is_horizontal_space(c: u8) -> bool { + c == b' ' || c == b'\t' +} diff --git a/vendor/winnow/examples/ini/bench.rs b/vendor/winnow/examples/ini/bench.rs new file mode 100644 index 0000000..3c7eab8 --- /dev/null +++ b/vendor/winnow/examples/ini/bench.rs @@ -0,0 +1,61 @@ +use winnow::combinator::repeat; +use winnow::prelude::*; + +mod parser; +mod parser_str; + +fn bench_ini(c: &mut criterion::Criterion) { + let str = "[owner] +name=John Doe +organization=Acme Widgets Inc. + +[database] +server=192.0.2.62 +port=143 +file=payroll.dat +\0"; + + let mut group = c.benchmark_group("ini"); + group.throughput(criterion::Throughput::Bytes(str.len() as u64)); + group.bench_function(criterion::BenchmarkId::new("bytes", str.len()), |b| { + 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()) + }); +} + +fn bench_ini_keys_and_values(c: &mut criterion::Criterion) { + let str = "server=192.0.2.62 +port=143 +file=payroll.dat +\0"; + + fn acc<'s>(i: &mut parser::Stream<'s>) -> PResult<Vec<(&'s str, &'s str)>> { + repeat(0.., parser::key_value).parse_next(i) + } + + let mut group = c.benchmark_group("ini keys and values"); + group.throughput(criterion::Throughput::Bytes(str.len() as u64)); + group.bench_function(criterion::BenchmarkId::new("bytes", str.len()), |b| { + b.iter(|| acc.parse_peek(str.as_bytes()).unwrap()); + }); +} + +fn bench_ini_key_value(c: &mut criterion::Criterion) { + let str = "server=192.0.2.62\n"; + + let mut group = c.benchmark_group("ini key value"); + group.throughput(criterion::Throughput::Bytes(str.len() as u64)); + group.bench_function(criterion::BenchmarkId::new("bytes", str.len()), |b| { + b.iter(|| parser::key_value.parse_peek(str.as_bytes()).unwrap()); + }); +} + +criterion::criterion_group!( + benches, + bench_ini, + bench_ini_keys_and_values, + bench_ini_key_value +); +criterion::criterion_main!(benches); diff --git a/vendor/winnow/examples/ini/main.rs b/vendor/winnow/examples/ini/main.rs new file mode 100644 index 0000000..8f61d18 --- /dev/null +++ b/vendor/winnow/examples/ini/main.rs @@ -0,0 +1,60 @@ +use winnow::prelude::*; + +mod parser; +mod parser_str; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + + let input = args.input.as_deref().unwrap_or("1 + 1"); + + if args.binary { + match parser::categories.parse(input.as_bytes()) { + Ok(result) => { + println!(" {:?}", result); + } + Err(err) => { + println!(" {:?}", err); + } + } + } else { + match parser_str::categories.parse(input) { + Ok(result) => { + println!(" {:?}", result); + } + Err(err) => { + println!(" {}", err); + } + } + } + + Ok(()) +} + +#[derive(Default)] +struct Args { + input: Option<String>, + binary: bool, +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Long("binary") => { + res.binary = true; + } + Value(input) => { + res.input = Some(input.string()?); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} diff --git a/vendor/winnow/examples/ini/parser.rs b/vendor/winnow/examples/ini/parser.rs new file mode 100644 index 0000000..a782d6e --- /dev/null +++ b/vendor/winnow/examples/ini/parser.rs @@ -0,0 +1,146 @@ +use std::collections::HashMap; +use std::str; + +use winnow::prelude::*; +use winnow::{ + ascii::{alphanumeric1 as alphanumeric, multispace0 as multispace, space0 as space}, + combinator::opt, + combinator::repeat, + combinator::{delimited, separated_pair, terminated}, + token::take_while, +}; + +pub type Stream<'i> = &'i [u8]; + +pub fn categories<'s>(i: &mut Stream<'s>) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> { + repeat( + 0.., + separated_pair( + category, + opt(multispace), + repeat(0.., terminated(key_value, opt(multispace))), + ), + ) + .parse_next(i) +} + +fn category<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { + delimited('[', take_while(0.., |c| c != b']'), ']') + .try_map(str::from_utf8) + .parse_next(i) +} + +pub 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';') + .try_map(str::from_utf8) + .parse_next(i)?; + let _ = opt((';', take_while(0.., |c| c != b'\n'))).parse_next(i)?; + Ok((key, val)) +} + +#[test] +fn parse_category_test() { + let ini_file = &b"[category] + +parameter=value +key = value2"[..]; + + let ini_without_category = &b"\n\nparameter=value +key = value2"[..]; + + let res = category.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_category, "category"))); +} + +#[test] +fn parse_key_value_test() { + let ini_file = &b"parameter=value +key = value2"[..]; + + let ini_without_key_value = &b"\nkey = value2"[..]; + + let res = key_value.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_space_test() { + let ini_file = &b"parameter = value +key = value2"[..]; + + let ini_without_key_value = &b"\nkey = value2"[..]; + + let res = key_value.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_comment_test() { + let ini_file = &b"parameter=value;abc +key = value2"[..]; + + let ini_without_key_value = &b"\nkey = value2"[..]; + + let res = key_value.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_multiple_categories_test() { + let ini_file = &b"[abcd] + +parameter=value;abc + +key = value2 + +[category] +parameter3=value3 +key4 = value4 +"[..]; + + let ini_after_parser = &b""[..]; + + let res = categories.parse_peek(ini_file); + //println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o), + _ => println!("error"), + } + + let mut expected_1: HashMap<&str, &str> = HashMap::new(); + expected_1.insert("parameter", "value"); + expected_1.insert("key", "value2"); + let mut expected_2: HashMap<&str, &str> = HashMap::new(); + expected_2.insert("parameter3", "value3"); + expected_2.insert("key4", "value4"); + let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new(); + expected_h.insert("abcd", expected_1); + expected_h.insert("category", expected_2); + assert_eq!(res, Ok((ini_after_parser, expected_h))); +} diff --git a/vendor/winnow/examples/ini/parser_str.rs b/vendor/winnow/examples/ini/parser_str.rs new file mode 100644 index 0000000..8f7b9ce --- /dev/null +++ b/vendor/winnow/examples/ini/parser_str.rs @@ -0,0 +1,208 @@ +use std::collections::HashMap; + +use winnow::prelude::*; +use winnow::{ + ascii::{alphanumeric1 as alphanumeric, space0 as space}, + combinator::opt, + combinator::repeat, + combinator::{delimited, terminated}, + token::{take_till0, take_while}, +}; + +pub type Stream<'i> = &'i str; + +pub fn categories<'s>( + input: &mut Stream<'s>, +) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> { + repeat(0.., category_and_keys).parse_next(input) +} + +fn category_and_keys<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, HashMap<&'s str, &'s str>)> { + (category, keys_and_values).parse_next(i) +} + +fn category<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { + terminated( + delimited('[', take_while(0.., |c| c != ']'), ']'), + opt(take_while(1.., [' ', '\r', '\n'])), + ) + .parse_next(i) +} + +fn keys_and_values<'s>(input: &mut Stream<'s>) -> PResult<HashMap<&'s str, &'s str>> { + repeat(0.., key_value).parse_next(input) +} + +fn key_value<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, &'s str)> { + let key = alphanumeric.parse_next(i)?; + let _ = (opt(space), "=", opt(space)).parse_next(i)?; + let val = take_till0(is_line_ending_or_comment).parse_next(i)?; + let _ = opt(space).parse_next(i)?; + let _ = opt((";", not_line_ending)).parse_next(i)?; + let _ = opt(space_or_line_ending).parse_next(i)?; + + Ok((key, val)) +} + +fn is_line_ending_or_comment(chr: char) -> bool { + chr == ';' || chr == '\n' +} + +fn not_line_ending<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { + take_while(0.., |c| c != '\r' && c != '\n').parse_next(i) +} + +fn space_or_line_ending<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { + take_while(1.., [' ', '\r', '\n']).parse_next(i) +} + +#[test] +fn parse_category_test() { + let ini_file = "[category] + +parameter=value +key = value2"; + + let ini_without_category = "parameter=value +key = value2"; + + let res = category.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_category, "category"))); +} + +#[test] +fn parse_key_value_test() { + let ini_file = "parameter=value +key = value2"; + + let ini_without_key_value = "key = value2"; + + let res = key_value.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_space_test() { + let ini_file = "parameter = value +key = value2"; + + let ini_without_key_value = "key = value2"; + + let res = key_value.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_key_value_with_comment_test() { + let ini_file = "parameter=value;abc +key = value2"; + + let ini_without_key_value = "key = value2"; + + let res = key_value.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), + _ => println!("error"), + } + + assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); +} + +#[test] +fn parse_multiple_keys_and_values_test() { + let ini_file = "parameter=value;abc + +key = value2 + +[category]"; + + let ini_without_key_value = "[category]"; + + let res = keys_and_values.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + let mut expected: HashMap<&str, &str> = HashMap::new(); + expected.insert("parameter", "value"); + expected.insert("key", "value2"); + assert_eq!(res, Ok((ini_without_key_value, expected))); +} + +#[test] +fn parse_category_then_multiple_keys_and_values_test() { + //FIXME: there can be an empty line or a comment line after a category + let ini_file = "[abcd] +parameter=value;abc + +key = value2 + +[category]"; + + let ini_after_parser = "[category]"; + + let res = category_and_keys.parse_peek(ini_file); + println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + let mut expected_h: HashMap<&str, &str> = HashMap::new(); + expected_h.insert("parameter", "value"); + expected_h.insert("key", "value2"); + assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h)))); +} + +#[test] +fn parse_multiple_categories_test() { + let ini_file = "[abcd] + +parameter=value;abc + +key = value2 + +[category] +parameter3=value3 +key4 = value4 +"; + + let res = categories.parse_peek(ini_file); + //println!("{:?}", res); + match res { + Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), + _ => println!("error"), + } + + let mut expected_1: HashMap<&str, &str> = HashMap::new(); + expected_1.insert("parameter", "value"); + expected_1.insert("key", "value2"); + let mut expected_2: HashMap<&str, &str> = HashMap::new(); + expected_2.insert("parameter3", "value3"); + expected_2.insert("key4", "value4"); + let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new(); + expected_h.insert("abcd", expected_1); + expected_h.insert("category", expected_2); + assert_eq!(res, Ok(("", expected_h))); +} diff --git a/vendor/winnow/examples/iterator.rs b/vendor/winnow/examples/iterator.rs new file mode 100644 index 0000000..5c8c731 --- /dev/null +++ b/vendor/winnow/examples/iterator.rs @@ -0,0 +1,77 @@ +use std::collections::HashMap; +use std::iter::Iterator; + +use winnow::ascii::alphanumeric1; +use winnow::combinator::iterator; +use winnow::combinator::{separated_pair, terminated}; +use winnow::prelude::*; + +fn main() { + let mut data = "abcabcabcabc"; + + fn parser<'s>(i: &mut &'s str) -> PResult<&'s str> { + "abc".parse_next(i) + } + + // `from_fn` (available from Rust 1.34) can create an iterator + // from a closure + let it = std::iter::from_fn(move || { + match parser.parse_next(&mut data) { + // when successful, a parser returns a tuple of + // the remaining input and the output value. + // So we replace the captured input data with the + // remaining input, to be parsed on the next call + Ok(o) => Some(o), + _ => None, + } + }); + + for value in it { + println!("parser returned: {}", value); + } + + println!("\n********************\n"); + + let mut data = "abcabcabcabc"; + + // if `from_fn` is not available, it is possible to fold + // over an iterator of functions + let res = std::iter::repeat(parser) + .take(3) + .try_fold(Vec::new(), |mut acc, mut parser| { + parser.parse_next(&mut data).map(|o| { + acc.push(o); + acc + }) + }); + + // will print "parser iterator returned: Ok(("abc", ["abc", "abc", "abc"]))" + println!("\nparser iterator returned: {:?}", res); + + println!("\n********************\n"); + + let data = "key1:value1,key2:value2,key3:value3,;"; + + // `winnow::combinator::iterator` will return an iterator + // producing the parsed values. Compared to the previous + // solutions: + // - we can work with a normal iterator like `from_fn` + // - we can get the remaining input afterwards, like with the `try_fold` trick + let mut winnow_it = iterator( + data, + terminated(separated_pair(alphanumeric1, ":", alphanumeric1), ","), + ); + + let res = winnow_it + .map(|(k, v)| (k.to_uppercase(), v)) + .collect::<HashMap<_, _>>(); + + let parser_result: PResult<(_, _), ()> = winnow_it.finish(); + 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 + ); +} diff --git a/vendor/winnow/examples/json/bench.rs b/vendor/winnow/examples/json/bench.rs new file mode 100644 index 0000000..d074ba5 --- /dev/null +++ b/vendor/winnow/examples/json/bench.rs @@ -0,0 +1,70 @@ +use winnow::prelude::*; +use winnow::Partial; + +mod json; +mod parser; +mod parser_dispatch; +mod parser_partial; + +fn json_bench(c: &mut criterion::Criterion) { + let data = [("small", SMALL), ("canada", CANADA)]; + let mut group = c.benchmark_group("json"); + for (name, sample) in data { + 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()); + }); + group.bench_with_input( + criterion::BenchmarkId::new("context", name), + &len, + |b, _| { + type Error<'i> = winnow::error::ContextError<parser::Stream<'i>>; + + b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap()); + }, + ); + group.bench_with_input( + criterion::BenchmarkId::new("dispatch", name), + &len, + |b, _| { + type Error<'i> = winnow::error::InputError<parser_dispatch::Stream<'i>>; + + b.iter(|| parser_dispatch::json::<Error>.parse_peek(sample).unwrap()); + }, + ); + group.bench_with_input( + criterion::BenchmarkId::new("streaming", name), + &len, + |b, _| { + type Error<'i> = winnow::error::InputError<parser_partial::Stream<'i>>; + + b.iter(|| { + parser_partial::json::<Error> + .parse_peek(Partial::new(sample)) + .unwrap() + }); + }, + ); + } + group.finish(); +} + +const SMALL: &str = " { \"a\"\t: 42, + \"b\": [ \"x\", \"y\", 12 ,\"\\u2014\", \"\\uD83D\\uDE10\"] , + \"c\": { \"hello\" : \"world\" + } + } "; + +const CANADA: &str = include_str!("../../third_party/nativejson-benchmark/data/canada.json"); + +criterion::criterion_group!(benches, json_bench,); +criterion::criterion_main!(benches); diff --git a/vendor/winnow/examples/json/json.rs b/vendor/winnow/examples/json/json.rs new file mode 100644 index 0000000..6912d60 --- /dev/null +++ b/vendor/winnow/examples/json/json.rs @@ -0,0 +1,11 @@ +use std::collections::HashMap; + +#[derive(Debug, PartialEq, Clone)] +pub enum JsonValue { + Null, + Boolean(bool), + Str(String), + Num(f64), + Array(Vec<JsonValue>), + Object(HashMap<String, JsonValue>), +} diff --git a/vendor/winnow/examples/json/main.rs b/vendor/winnow/examples/json/main.rs new file mode 100644 index 0000000..be431e8 --- /dev/null +++ b/vendor/winnow/examples/json/main.rs @@ -0,0 +1,98 @@ +mod json; +mod parser; +mod parser_dispatch; +#[allow(dead_code)] +mod parser_partial; + +use winnow::error::ErrorKind; +use winnow::prelude::*; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + + let data = args.input.as_deref().unwrap_or(if args.invalid { + " { \"a\"\t: 42, + \"b\": [ \"x\", \"y\", 12 ] , + \"c\": { 1\"hello\" : \"world\" + } + } " + } else { + " { \"a\"\t: 42, + \"b\": [ \"x\", \"y\", 12 ] , + \"c\": { \"hello\" : \"world\" + } + } " + }); + + let result = match args.implementation { + Impl::Naive => parser::json::<ErrorKind>.parse(data), + Impl::Dispatch => parser_dispatch::json::<ErrorKind>.parse(data), + }; + match result { + Ok(json) => { + println!("{:#?}", json); + } + Err(err) => { + if args.pretty { + println!("{}", err); + } else { + println!("{:#?}", err); + } + } + } + + Ok(()) +} + +#[derive(Default)] +struct Args { + input: Option<String>, + invalid: bool, + pretty: bool, + implementation: Impl, +} + +enum Impl { + Naive, + Dispatch, +} + +impl Default for Impl { + fn default() -> Self { + Self::Naive + } +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Long("invalid") => { + res.invalid = true; + } + Long("pretty") => { + // Only case where pretty matters + res.pretty = true; + res.invalid = true; + } + Long("impl") => { + res.implementation = args.value()?.parse_with(|s| match s { + "naive" => Ok(Impl::Naive), + "dispatch" => Ok(Impl::Dispatch), + _ => Err("expected `naive`, `dispatch`"), + })?; + } + Value(input) => { + res.input = Some(input.string()?); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} diff --git a/vendor/winnow/examples/json/parser.rs b/vendor/winnow/examples/json/parser.rs new file mode 100644 index 0000000..8aa3bd3 --- /dev/null +++ b/vendor/winnow/examples/json/parser.rs @@ -0,0 +1,322 @@ +use std::collections::HashMap; +use std::str; + +use winnow::prelude::*; +use winnow::{ + ascii::float, + combinator::alt, + combinator::cut_err, + combinator::{delimited, preceded, separated_pair, terminated}, + combinator::{fold_repeat, separated0}, + error::{AddContext, ParserError}, + token::{any, none_of, take, take_while}, +}; + +use crate::json::JsonValue; + +pub 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: +/// `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 +/// examples shows custom error types later on!) +/// +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + delimited(ws, json_value, ws).parse_next(input) +} + +/// `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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + // `alt` combines the each value parser. It returns the result of the first + // successful parser, or an error + alt(( + null.value(JsonValue::Null), + boolean.map(JsonValue::Boolean), + string.map(JsonValue::Str), + float.map(JsonValue::Num), + array.map(JsonValue::Array), + object.map(JsonValue::Object), + )) + .parse_next(input) +} + +/// `tag(string)` generates a parser that recognizes 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> { + // This is a parser that returns `"null"` if it sees the string "null", and + // an error otherwise + "null".parse_next(input) +} + +/// We can combine `tag` with other functions, like `value` which returns a given constant value on +/// success. +fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> { + // This is a parser that returns `true` if it sees the string "true", and + // an error otherwise + let parse_true = "true".value(true); + + // This is a parser that returns `false` if it sees the string "false", and + // an error otherwise + let parse_false = "false".value(false); + + 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 +/// character, before the string (using `preceded`) and after the string (using `terminated`). +fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<String, E> { + preceded( + '\"', + // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to + // combinators like `alt` that they should not try other parsers. We were in the + // right branch (since we found the `"` character) but encountered an error when + // parsing the string + cut_err(terminated( + fold_repeat(0.., character, String::new, |mut string, c| { + string.push(c); + string + }), + '\"', + )), + ) + // `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") + .parse_next(input) +} + +/// You can mix the above declarative parsing with an imperative style to handle more unique cases, +/// like escaping +fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + let c = none_of('\"').parse_next(input)?; + if c == '\\' { + alt(( + any.verify_map(|c| { + Some(match c { + '"' | '\\' | '/' => c, + 'b' => '\x08', + 'f' => '\x0C', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return None, + }) + }), + preceded('u', unicode_escape), + )) + .parse_next(input) + } else { + Ok(c) + } +} + +fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + alt(( + // Not a surrogate + u16_hex + .verify(|cp| !(0xD800..0xE000).contains(cp)) + .map(|cp| cp as u32), + // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details + separated_pair(u16_hex, "\\u", u16_hex) + .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low)) + .map(|(high, low)| { + let high_ten = (high as u32) - 0xD800; + let low_ten = (low as u32) - 0xDC00; + (high_ten << 10) + low_ten + 0x10000 + }), + )) + .verify_map( + // Could be probably replaced with .unwrap() or _unchecked due to the verify checks + std::char::from_u32, + ) + .parse_next(input) +} + +fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> { + take(4usize) + .verify_map(|s| u16::from_str_radix(s, 16).ok()) + .parse_next(input) +} + +/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly, +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<Vec<JsonValue>, E> { + preceded( + ('[', ws), + cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), + ) + .context("array") + .parse_next(input) +} + +fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<HashMap<String, JsonValue>, E> { + preceded( + ('{', ws), + cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), + ) + .context("object") + .parse_next(input) +} + +fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<(String, JsonValue), E> { + separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) +} + +/// Parser combinators are constructed from the bottom up: +/// first we write parsers for the smallest elements (here a space character), +/// then we'll combine them in larger parsers +fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { + // Combinators like `take_while` return a function. That function is the + // parser,to which we can pass the input + take_while(0.., WS).parse_next(input) +} + +const WS: &[char] = &[' ', '\t', '\r', '\n']; + +#[cfg(test)] +mod test { + #[allow(clippy::useless_attribute)] + #[allow(dead_code)] // 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>; + + #[test] + fn json_string() { + assert_eq!( + string::<Error<'_>>.parse_peek("\"\""), + Ok(("", "".to_string())) + ); + assert_eq!( + string::<Error<'_>>.parse_peek("\"abc\""), + Ok(("", "abc".to_string())) + ); + assert_eq!( + 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())), + ); + assert_eq!( + string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""), + Ok(("", "😐".to_string())) + ); + + 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] + fn json_object() { + use JsonValue::{Num, Object, Str}; + + let input = r#"{"a":42,"b":"x"}"#; + + let expected = Object( + vec![ + ("a".to_string(), Num(42.0)), + ("b".to_string(), Str("x".to_string())), + ] + .into_iter() + .collect(), + ); + + assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected))); + } + + #[test] + fn json_array() { + use JsonValue::{Array, Num, Str}; + + let input = r#"[42,"x"]"#; + + let expected = Array(vec![Num(42.0), Str("x".to_string())]); + + assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected))); + } + + #[test] + fn json_whitespace() { + use JsonValue::{Array, Boolean, Null, Num, Object, Str}; + + let input = r#" + { + "null" : null, + "true" :true , + "false": false , + "number" : 123e4 , + "string" : " abc 123 " , + "array" : [ false , 1 , "two" ] , + "object" : { "a" : 1.0 , "b" : "c" } , + "empty_array" : [ ] , + "empty_object" : { } + } + "#; + + assert_eq!( + 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())), + ( + "array".to_string(), + Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) + ), + ( + "object".to_string(), + Object( + vec![ + ("a".to_string(), Num(1.0)), + ("b".to_string(), Str("c".to_string())), + ] + .into_iter() + .collect() + ) + ), + ("empty_array".to_string(), Array(vec![]),), + ("empty_object".to_string(), Object(HashMap::new()),), + ] + .into_iter() + .collect() + ) + )) + ); + } +} diff --git a/vendor/winnow/examples/json/parser_dispatch.rs b/vendor/winnow/examples/json/parser_dispatch.rs new file mode 100644 index 0000000..6fa722b --- /dev/null +++ b/vendor/winnow/examples/json/parser_dispatch.rs @@ -0,0 +1,329 @@ +use std::collections::HashMap; +use std::str; + +use winnow::prelude::*; +use winnow::{ + ascii::float, + combinator::cut_err, + combinator::fail, + combinator::peek, + combinator::success, + combinator::{alt, dispatch}, + combinator::{delimited, preceded, separated_pair, terminated}, + combinator::{fold_repeat, separated0}, + error::{AddContext, ParserError}, + token::{any, none_of, take, take_while}, +}; + +use crate::json::JsonValue; + +pub 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: +/// `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 +/// examples shows custom error types later on!) +/// +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + delimited(ws, json_value, ws).parse_next(input) +} + +/// `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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + // `dispatch` gives you `match`-like behavior compared to `alt` successively trying different + // implementations. + dispatch!(peek(any); + 'n' => null.value(JsonValue::Null), + 't' => true_.map(JsonValue::Boolean), + 'f' => false_.map(JsonValue::Boolean), + '"' => string.map(JsonValue::Str), + '+' => float.map(JsonValue::Num), + '-' => float.map(JsonValue::Num), + '0'..='9' => float.map(JsonValue::Num), + '[' => array.map(JsonValue::Array), + '{' => object.map(JsonValue::Object), + _ => fail, + ) + .parse_next(input) +} + +/// `tag(string)` generates a parser that recognizes 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> { + // This is a parser that returns `"null"` if it sees the string "null", and + // an error otherwise + "null".parse_next(input) +} + +/// We can combine `tag` with other functions, like `value` which returns a given constant value on +/// success. +fn true_<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> { + // This is a parser that returns `true` if it sees the string "true", and + // an error otherwise + "true".value(true).parse_next(input) +} + +/// We can combine `tag` with other functions, like `value` which returns a given constant value on +/// success. +fn false_<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> { + // This is a parser that returns `false` if it sees the string "false", and + // an error otherwise + "false".value(false).parse_next(input) +} + +/// This parser gathers all `char`s up into a `String`with a parse to recognize 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>>( + input: &mut Stream<'i>, +) -> PResult<String, E> { + preceded( + '\"', + // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to + // combinators like `alt` that they should not try other parsers. We were in the + // right branch (since we found the `"` character) but encountered an error when + // parsing the string + cut_err(terminated( + fold_repeat(0.., character, String::new, |mut string, c| { + string.push(c); + string + }), + '\"', + )), + ) + // `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") + .parse_next(input) +} + +/// You can mix the above declarative parsing with an imperative style to handle more unique cases, +/// like escaping +fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + let c = none_of('\"').parse_next(input)?; + if c == '\\' { + dispatch!(any; + '"' => success('"'), + '\\' => success('\\'), + '/' => success('/'), + 'b' => success('\x08'), + 'f' => success('\x0C'), + 'n' => success('\n'), + 'r' => success('\r'), + 't' => success('\t'), + 'u' => unicode_escape, + _ => fail, + ) + .parse_next(input) + } else { + Ok(c) + } +} + +fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + alt(( + // Not a surrogate + u16_hex + .verify(|cp| !(0xD800..0xE000).contains(cp)) + .map(|cp| cp as u32), + // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details + separated_pair(u16_hex, "\\u", u16_hex) + .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low)) + .map(|(high, low)| { + let high_ten = (high as u32) - 0xD800; + let low_ten = (low as u32) - 0xDC00; + (high_ten << 10) + low_ten + 0x10000 + }), + )) + .verify_map( + // Could be probably replaced with .unwrap() or _unchecked due to the verify checks + std::char::from_u32, + ) + .parse_next(input) +} + +fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> { + take(4usize) + .verify_map(|s| u16::from_str_radix(s, 16).ok()) + .parse_next(input) +} + +/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly, +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<Vec<JsonValue>, E> { + preceded( + ('[', ws), + cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), + ) + .context("array") + .parse_next(input) +} + +fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<HashMap<String, JsonValue>, E> { + preceded( + ('{', ws), + cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), + ) + .context("object") + .parse_next(input) +} + +fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<(String, JsonValue), E> { + separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) +} + +/// Parser combinators are constructed from the bottom up: +/// first we write parsers for the smallest elements (here a space character), +/// then we'll combine them in larger parsers +fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { + // Combinators like `take_while` return a function. That function is the + // parser,to which we can pass the input + take_while(0.., WS).parse_next(input) +} + +const WS: &[char] = &[' ', '\t', '\r', '\n']; + +#[cfg(test)] +mod test { + #[allow(clippy::useless_attribute)] + #[allow(dead_code)] // 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>; + + #[test] + fn json_string() { + assert_eq!( + string::<Error<'_>>.parse_peek("\"\""), + Ok(("", "".to_string())) + ); + assert_eq!( + string::<Error<'_>>.parse_peek("\"abc\""), + Ok(("", "abc".to_string())) + ); + assert_eq!( + 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())), + ); + assert_eq!( + string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""), + Ok(("", "😐".to_string())) + ); + + 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] + fn json_object() { + use JsonValue::{Num, Object, Str}; + + let input = r#"{"a":42,"b":"x"}"#; + + let expected = Object( + vec![ + ("a".to_string(), Num(42.0)), + ("b".to_string(), Str("x".to_string())), + ] + .into_iter() + .collect(), + ); + + assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected))); + } + + #[test] + fn json_array() { + use JsonValue::{Array, Num, Str}; + + let input = r#"[42,"x"]"#; + + let expected = Array(vec![Num(42.0), Str("x".to_string())]); + + assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected))); + } + + #[test] + fn json_whitespace() { + use JsonValue::{Array, Boolean, Null, Num, Object, Str}; + + let input = r#" + { + "null" : null, + "true" :true , + "false": false , + "number" : 123e4 , + "string" : " abc 123 " , + "array" : [ false , 1 , "two" ] , + "object" : { "a" : 1.0 , "b" : "c" } , + "empty_array" : [ ] , + "empty_object" : { } + } + "#; + + assert_eq!( + 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())), + ( + "array".to_string(), + Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) + ), + ( + "object".to_string(), + Object( + vec![ + ("a".to_string(), Num(1.0)), + ("b".to_string(), Str("c".to_string())), + ] + .into_iter() + .collect() + ) + ), + ("empty_array".to_string(), Array(vec![]),), + ("empty_object".to_string(), Object(HashMap::new()),), + ] + .into_iter() + .collect() + ) + )) + ); + } +} diff --git a/vendor/winnow/examples/json/parser_partial.rs b/vendor/winnow/examples/json/parser_partial.rs new file mode 100644 index 0000000..3538d8e --- /dev/null +++ b/vendor/winnow/examples/json/parser_partial.rs @@ -0,0 +1,348 @@ +use std::collections::HashMap; +use std::str; + +use winnow::prelude::*; +use winnow::{ + ascii::float, + combinator::alt, + combinator::{cut_err, rest}, + combinator::{delimited, preceded, separated_pair, terminated}, + combinator::{fold_repeat, separated0}, + error::{AddContext, ParserError}, + stream::Partial, + token::{any, none_of, take, take_while}, +}; + +use crate::json::JsonValue; + +pub 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: +/// `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 +/// examples shows custom error types later on!) +/// +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + delimited(ws, json_value, ws_or_eof).parse_next(input) +} + +/// `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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + // `alt` combines the each value parser. It returns the result of the first + // successful parser, or an error + alt(( + null.value(JsonValue::Null), + boolean.map(JsonValue::Boolean), + string.map(JsonValue::Str), + float.map(JsonValue::Num), + array.map(JsonValue::Array), + object.map(JsonValue::Object), + )) + .parse_next(input) +} + +/// `tag(string)` generates a parser that recognizes 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> { + // This is a parser that returns `"null"` if it sees the string "null", and + // an error otherwise + "null".parse_next(input) +} + +/// We can combine `tag` with other functions, like `value` which returns a given constant value on +/// success. +fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> { + // This is a parser that returns `true` if it sees the string "true", and + // an error otherwise + let parse_true = "true".value(true); + + // This is a parser that returns `false` if it sees the string "false", and + // an error otherwise + let parse_false = "false".value(false); + + 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 +/// character, before the string (using `preceded`) and after the string (using `terminated`). +fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<String, E> { + preceded( + '\"', + // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to + // combinators like `alt` that they should not try other parsers. We were in the + // right branch (since we found the `"` character) but encountered an error when + // parsing the string + cut_err(terminated( + fold_repeat(0.., character, String::new, |mut string, c| { + string.push(c); + string + }), + '\"', + )), + ) + // `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") + .parse_next(input) +} + +/// You can mix the above declarative parsing with an imperative style to handle more unique cases, +/// like escaping +fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + let c = none_of('\"').parse_next(input)?; + if c == '\\' { + alt(( + any.verify_map(|c| { + Some(match c { + '"' | '\\' | '/' => c, + 'b' => '\x08', + 'f' => '\x0C', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return None, + }) + }), + preceded('u', unicode_escape), + )) + .parse_next(input) + } else { + Ok(c) + } +} + +fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + alt(( + // Not a surrogate + u16_hex + .verify(|cp| !(0xD800..0xE000).contains(cp)) + .map(|cp| cp as u32), + // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details + separated_pair(u16_hex, "\\u", u16_hex) + .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low)) + .map(|(high, low)| { + let high_ten = (high as u32) - 0xD800; + let low_ten = (low as u32) - 0xDC00; + (high_ten << 10) + low_ten + 0x10000 + }), + )) + .verify_map( + // Could be probably replaced with .unwrap() or _unchecked due to the verify checks + std::char::from_u32, + ) + .parse_next(input) +} + +fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> { + take(4usize) + .verify_map(|s| u16::from_str_radix(s, 16).ok()) + .parse_next(input) +} + +/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly, +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<Vec<JsonValue>, E> { + preceded( + ('[', ws), + cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), + ) + .context("array") + .parse_next(input) +} + +fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<HashMap<String, JsonValue>, E> { + preceded( + ('{', ws), + cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), + ) + .context("object") + .parse_next(input) +} + +fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<(String, JsonValue), E> { + separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) +} + +/// Parser combinators are constructed from the bottom up: +/// first we write parsers for the smallest elements (here a space character), +/// then we'll combine them in larger parsers +fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { + // Combinators like `take_while` return a function. That function is the + // parser,to which we can pass the input + take_while(0.., WS).parse_next(input) +} + +fn ws_or_eof<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { + rest.verify(|s: &str| s.chars().all(|c| WS.contains(&c))) + .parse_next(input) +} + +const WS: &[char] = &[' ', '\t', '\r', '\n']; + +#[cfg(test)] +mod test { + #[allow(clippy::useless_attribute)] + #[allow(dead_code)] // 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>>; + + #[test] + fn json_string() { + assert_eq!( + string::<Error<'_>>.parse_peek(Partial::new("\"\"")), + Ok((Partial::new(""), "".to_string())) + ); + assert_eq!( + string::<Error<'_>>.parse_peek(Partial::new("\"abc\"")), + Ok((Partial::new(""), "abc".to_string())) + ); + assert_eq!( + 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() + )), + ); + assert_eq!( + string::<Error<'_>>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")), + Ok((Partial::new(""), "😐".to_string())) + ); + + 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<'_>> + .parse_peek(Partial::new("\"\\uD800\"")) + .is_err()); + assert!(string::<Error<'_>> + .parse_peek(Partial::new("\"\\uD800\\uD800\"")) + .is_err()); + assert!(string::<Error<'_>> + .parse_peek(Partial::new("\"\\uDC00\"")) + .is_err()); + } + + #[test] + fn json_object() { + use JsonValue::{Num, Object, Str}; + + let input = r#"{"a":42,"b":"x"}"#; + + let expected = Object( + vec![ + ("a".to_string(), Num(42.0)), + ("b".to_string(), Str("x".to_string())), + ] + .into_iter() + .collect(), + ); + + assert_eq!( + json::<Error<'_>>.parse_peek(Partial::new(input)), + Ok((Partial::new(""), expected)) + ); + } + + #[test] + fn json_array() { + use JsonValue::{Array, Num, Str}; + + let input = r#"[42,"x"]"#; + + let expected = Array(vec![Num(42.0), Str("x".to_string())]); + + assert_eq!( + json::<Error<'_>>.parse_peek(Partial::new(input)), + Ok((Partial::new(""), expected)) + ); + } + + #[test] + fn json_whitespace() { + use JsonValue::{Array, Boolean, Null, Num, Object, Str}; + + let input = r#" + { + "null" : null, + "true" :true , + "false": false , + "number" : 123e4 , + "string" : " abc 123 " , + "array" : [ false , 1 , "two" ] , + "object" : { "a" : 1.0 , "b" : "c" } , + "empty_array" : [ ] , + "empty_object" : { } + } + "#; + + assert_eq!( + 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())), + ( + "array".to_string(), + Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) + ), + ( + "object".to_string(), + Object( + vec![ + ("a".to_string(), Num(1.0)), + ("b".to_string(), Str("c".to_string())), + ] + .into_iter() + .collect() + ) + ), + ("empty_array".to_string(), Array(vec![]),), + ("empty_object".to_string(), Object(HashMap::new()),), + ] + .into_iter() + .collect() + ) + )) + ); + } +} diff --git a/vendor/winnow/examples/json_iterator.rs b/vendor/winnow/examples/json_iterator.rs new file mode 100644 index 0000000..b8b46f3 --- /dev/null +++ b/vendor/winnow/examples/json_iterator.rs @@ -0,0 +1,311 @@ +use std::collections::HashMap; + +use winnow::prelude::*; +use winnow::{ + ascii::{alphanumeric1 as alphanumeric, escaped, float}, + combinator::alt, + combinator::cut_err, + combinator::separated0, + combinator::{preceded, separated_pair, terminated}, + error::ParserError, + error::StrContext, + stream::Offset, + token::one_of, + token::{tag, take_while}, +}; + +use std::cell::Cell; +use std::str; + +#[derive(Clone, Debug)] +pub struct JsonValue<'a, 'b> { + input: &'a str, + pub offset: &'b Cell<usize>, +} + +impl<'a, 'b: 'a> JsonValue<'a, 'b> { + pub fn new(input: &'a str, offset: &'b Cell<usize>) -> JsonValue<'a, 'b> { + JsonValue { input, offset } + } + + pub fn offset(&self, input: &'a str) { + let offset = input.offset_from(&self.input); + self.offset.set(offset); + } + + pub fn data(&self) -> &'a str { + &self.input[self.offset.get()..] + } + + pub fn string(&self) -> Option<&'a str> { + println!("string()"); + let mut data = self.data(); + match string(&mut data) { + Ok(s) => { + self.offset(data); + println!("-> {}", s); + Some(s) + } + _ => None, + } + } + + pub fn boolean(&self) -> Option<bool> { + println!("boolean()"); + let mut data = self.data(); + match boolean(&mut data) { + Ok(o) => { + self.offset(data); + println!("-> {}", o); + Some(o) + } + _ => None, + } + } + + pub fn number(&self) -> Option<f64> { + println!("number()"); + let mut data = self.data(); + match float::<_, _, ()>.parse_next(&mut data) { + Ok(o) => { + self.offset(data); + println!("-> {}", o); + Some(o) + } + _ => None, + } + } + + pub fn array(&self) -> Option<impl Iterator<Item = JsonValue<'a, 'b>>> { + println!("array()"); + + let mut data = self.data(); + match tag::<_, _, ()>("[").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 v = self.clone(); + + Some(std::iter::from_fn(move || { + if done { + return None; + } + + // if we ignored one of the items, skip over the value + if v.offset.get() == previous { + println!("skipping value"); + if value(&mut data).is_ok() { + v.offset(data); + } + } + + if tag::<_, _, ()>("]").parse_next(&mut data).is_ok() { + println!("]"); + v.offset(data); + done = true; + return None; + } + + if first { + first = false; + } else { + match tag::<_, _, ()>(",").parse_next(&mut data) { + Ok(_) => { + println!(","); + v.offset(data); + } + Err(_) => { + done = true; + return None; + } + } + } + + println!("-> {}", v.data()); + previous = v.offset.get(); + Some(v.clone()) + })) + } + } + } + + 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) { + Err(_) => None, + Ok(_) => { + self.offset(data); + + println!("{{"); + + let mut first = true; + let mut done = false; + let mut previous = std::usize::MAX; + + let v = self.clone(); + + Some(std::iter::from_fn(move || { + if done { + return None; + } + + // if we ignored one of the items, skip over the value + if v.offset.get() == previous { + println!("skipping value"); + if value(&mut data).is_ok() { + v.offset(data); + } + } + + if tag::<_, _, ()>("}").parse_next(&mut data).is_ok() { + println!("}}"); + v.offset(data); + done = true; + return None; + } + + if first { + first = false; + } else { + match tag::<_, _, ()>(",").parse_next(&mut data) { + Ok(_) => { + println!(","); + v.offset(data); + } + Err(_) => { + done = true; + return None; + } + } + } + + match string(&mut data) { + Ok(key) => { + v.offset(data); + + match tag::<_, _, ()>(":").parse_next(&mut data) { + Err(_) => None, + Ok(_) => { + v.offset(data); + + previous = v.offset.get(); + + println!("-> {} => {}", key, v.data()); + Some((key, v.clone())) + } + } + } + _ => None, + } + })) + } + } + } +} + +fn sp<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> { + let chars = " \t\r\n"; + + take_while(0.., move |c| chars.contains(c)).parse_next(i) +} + +fn parse_str<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> { + escaped(alphanumeric, '\\', one_of(['"', 'n', '\\'])).parse_next(i) +} + +fn string<'s>(i: &mut &'s str) -> PResult<&'s str> { + preceded('\"', cut_err(terminated(parse_str, '\"'))) + .context(StrContext::Label("string")) + .parse_next(i) +} + +fn boolean(input: &mut &str) -> PResult<bool> { + alt(("false".map(|_| false), "true".map(|_| true))).parse_next(input) +} + +fn array(i: &mut &str) -> PResult<()> { + preceded( + '[', + cut_err(terminated( + separated0(value, preceded(sp, ',')), + preceded(sp, ']'), + )), + ) + .context(StrContext::Label("array")) + .parse_next(i) +} + +fn key_value<'s>(i: &mut &'s str) -> PResult<(&'s str, ())> { + separated_pair(preceded(sp, string), cut_err(preceded(sp, ':')), value).parse_next(i) +} + +fn hash(i: &mut &str) -> PResult<()> { + preceded( + '{', + cut_err(terminated( + separated0(key_value, preceded(sp, ',')), + preceded(sp, '}'), + )), + ) + .context(StrContext::Label("map")) + .parse_next(i) +} + +fn value(i: &mut &str) -> PResult<()> { + preceded( + sp, + alt(( + hash, + array, + string.map(|_| ()), + float::<_, f64, _>.map(|_| ()), + boolean.map(|_| ()), + )), + ) + .parse_next(i) +} + +/// object(input) -> iterator over (key, `JsonValue`) +/// array(input) -> iterator over `JsonValue` +/// +/// 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()) +fn main() { + /*let data = "{ + \"users\": { + \"user1\" : { \"city\": \"Nantes\", \"country\": \"France\" }, + \"user2\" : { \"city\": \"Bruxelles\", \"country\": \"Belgium\" }, + \"user3\": { \"city\": \"Paris\", \"country\": \"France\", \"age\": 30 } + }, + \"countries\": [\"France\", \"Belgium\"] + }"; + */ + let data = "{\"users\":{\"user1\":{\"city\":\"Nantes\",\"country\":\"France\"},\"user2\":{\"city\":\"Bruxelles\",\"country\":\"Belgium\"},\"user3\":{\"city\":\"Paris\",\"country\":\"France\",\"age\":30}},\"countries\":[\"France\",\"Belgium\"]}"; + + let offset = Cell::new(0); + { + let parser = JsonValue::new(data, &offset); + + if let Some(o) = parser.object() { + let s: HashMap<&str, &str> = o + .filter(|(k, _)| *k == "users") + .filter_map(|(_, v)| v.object()) + .flatten() + .filter_map(|(user, v)| v.object().map(|o| (user, o))) + .flat_map(|(user, o)| { + o.filter(|(k, _)| *k == "city") + .filter_map(move |(_, v)| v.string().map(|s| (user, s))) + }) + .collect(); + + println!("res = {:?}", s); + } + }; +} diff --git a/vendor/winnow/examples/ndjson/example.ndjson b/vendor/winnow/examples/ndjson/example.ndjson new file mode 100644 index 0000000..7f1fca0 --- /dev/null +++ b/vendor/winnow/examples/ndjson/example.ndjson @@ -0,0 +1,158 @@ +{"reason":"compiler-artifact","package_id":"proc-macro2 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/proc-macro2-d6a7808ec27a845d/build-script-build"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"proc-macro2 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["use_proc_macro","wrap_proc_macro"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/proc-macro2-e500f83d0dabcc00/out"} +{"reason":"compiler-artifact","package_id":"quote 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/quote-e70da9bace8e108a/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"libc 0.2.139 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","extra_traits","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/libc-ea536a8e67e0b7eb/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"unicode-ident 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-ident-1.0.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"unicode-ident","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-ident-1.0.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libunicode_ident-e72d3e3fa5fdcbf4.rlib","/home/epage/src/personal/winnow/target/debug/deps/libunicode_ident-e72d3e3fa5fdcbf4.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"proc-macro2 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"proc-macro2","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libproc_macro2-559e547b03a7ac1e.rlib","/home/epage/src/personal/winnow/target/debug/deps/libproc_macro2-559e547b03a7ac1e.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"libc 0.2.139 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["freebsd11","libc_priv_mod_use","libc_union","libc_const_size_of","libc_align","libc_int128","libc_core_cvoid","libc_packedN","libc_cfg_target_vendor","libc_non_exhaustive","libc_ptr_addr_of","libc_underscore_const_names","libc_const_extern_fn"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/libc-94d2f48bd38a8056/out"} +{"reason":"build-script-executed","package_id":"quote 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/quote-a2754428d152a498/out"} +{"reason":"compiler-artifact","package_id":"syn 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["clone-impls","default","derive","parsing","printing","proc-macro","quote"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/syn-c9e8af729632e4e4/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cfg-if-1.0.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"cfg-if","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cfg-if-1.0.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcfg_if-047a17fcf848a7e5.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"autocfg 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/autocfg-1.1.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"autocfg","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/autocfg-1.1.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libautocfg-25db3455927a66e1.rlib","/home/epage/src/personal/winnow/target/debug/deps/libautocfg-25db3455927a66e1.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde_derive 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/serde_derive-fcc2f4aec2a2d4ab/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","derive","serde_derive","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/serde-3f78a53b92e21d4d/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"libc 0.2.139 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"libc","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","extra_traits","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblibc-5b6dd9f3e6fc0120.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"quote 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quote","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquote-9a0c3a96b2cdc59c.rlib","/home/epage/src/personal/winnow/target/debug/deps/libquote-9a0c3a96b2cdc59c.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"syn 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["syn_disable_nightly_tests"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/syn-0a9a191063f1b2fc/out"} +{"reason":"build-script-executed","package_id":"serde_derive 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/serde_derive-ead5e900bac8546f/out"} +{"reason":"build-script-executed","package_id":"serde 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/serde-03f4af86861cbc3e/out"} +{"reason":"compiler-artifact","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/thiserror-66462325c558a4d5/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"ryu 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ryu-1.0.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ryu","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ryu-1.0.11/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libryu-0e7b0d46c4589f15.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"crossbeam-utils 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/crossbeam-utils-c9170234d86239e2/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/memchr-99ffc1dfd2c517c1/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde_json 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/serde_json-3051866d1babe853/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"memoffset 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/memoffset-37767cb27e2441e6/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bitflags-1.3.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bitflags","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bitflags-1.3.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbitflags-ea9a4e086e887550.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"itoa 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-1.0.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-1.0.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libitoa-69c375d2fd4189a8.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"crossbeam-epoch 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/crossbeam-epoch-ec0452f91ac732bb/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"ucd-trie 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ucd-trie","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libucd_trie-eb38f7c85a03bb9d.rlib","/home/epage/src/personal/winnow/target/debug/deps/libucd_trie-eb38f7c85a03bb9d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"io-lifetimes 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["close","default","libc","windows-sys"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/io-lifetimes-18d168bedd0c64e8/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/log-52c513d099058ca0/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/num-traits-77d338745cc017c3/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rayon-core 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/rayon-core-eecac7c574986103/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rustix 0.36.8 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","io-lifetimes","libc","std","termios","use-libc-auxv"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/rustix-32859c5d0061115d/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"syn 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["clone-impls","default","derive","parsing","printing","proc-macro","quote"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsyn-c9741af862298610.rlib","/home/epage/src/personal/winnow/target/debug/deps/libsyn-c9741af862298610.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/thiserror-37d8ebe1b01cc51d/out"} +{"reason":"build-script-executed","package_id":"memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["memchr_runtime_simd","memchr_runtime_sse2","memchr_runtime_sse42","memchr_runtime_avx"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/memchr-743d739a8480f48a/out"} +{"reason":"build-script-executed","package_id":"crossbeam-utils 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/crossbeam-utils-1975bdb7ecfbff2a/out"} +{"reason":"build-script-executed","package_id":"serde_json 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["limb_width_64"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/serde_json-3f569e9655a7cd7e/out"} +{"reason":"build-script-executed","package_id":"memoffset 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["tuple_ty","allow_clippy","maybe_uninit","doctests","raw_ref_macros"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/memoffset-27100c1e8e709074/out"} +{"reason":"build-script-executed","package_id":"crossbeam-epoch 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/crossbeam-epoch-7f643445aebfa633/out"} +{"reason":"compiler-artifact","package_id":"getrandom 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.2.7/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"getrandom","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.2.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libgetrandom-d2efdd8bbd217458.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["atomic_cas","has_atomics"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/log-b56bc898d3207792/out"} +{"reason":"build-script-executed","package_id":"io-lifetimes 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["io_safety_is_in_std","panic_in_const_fn"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/io-lifetimes-701560b8574ef205/out"} +{"reason":"compiler-artifact","package_id":"once_cell 1.15.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"once_cell","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","race","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libonce_cell-c9f9ea925d35da52.rlib","/home/epage/src/personal/winnow/target/debug/deps/libonce_cell-c9f9ea925d35da52.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/scopeguard-1.1.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"scopeguard","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/scopeguard-1.1.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libscopeguard-13a4bbff1ce24cc7.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"rayon-core 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/rayon-core-b008d521ccec1e7b/out"} +{"reason":"build-script-executed","package_id":"rustix 0.36.8 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["linux_raw","asm"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/rustix-e16304a596230d21/out"} +{"reason":"build-script-executed","package_id":"num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["has_i128","has_to_int_unchecked","has_reverse_bits","has_leading_trailing_ones","has_int_assignop_ref","has_div_euclid","has_copysign"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/num-traits-6888d3d0832572a9/out"} +{"reason":"compiler-artifact","package_id":"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"lazy_static","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblazy_static-afa8b761bb5d6b57.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-width-0.1.10/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"unicode-width","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-width-0.1.10/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libunicode_width-8dcd31c030e77d42.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"either 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/either-1.8.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"either","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/either-1.8.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["use_std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libeither-3c87d508139ef632.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"linux-raw-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/linux-raw-sys-0.1.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"linux-raw-sys","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/linux-raw-sys-0.1.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["errno","general","ioctl","no_std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblinux_raw_sys-297593f8ceab708f.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"num_cpus 1.13.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num_cpus-1.13.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"num_cpus","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num_cpus-1.13.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libnum_cpus-539d2b6f1744794b.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde_derive 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"serde_derive","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde_derive-7934298a61c1a1a2.so"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"thiserror-impl 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-impl-1.0.38/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"thiserror-impl","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-impl-1.0.38/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libthiserror_impl-213d86e6d4b69b5f.so"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"memchr","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libmemchr-ae9722f3894e3314.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"crossbeam-utils 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-utils","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_utils-59f0a08b7eefe073.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"memoffset 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"memoffset","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libmemoffset-4787845978faa8b8.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"io-lifetimes 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"io-lifetimes","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["close","default","libc","windows-sys"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libio_lifetimes-0d158e24024d572c.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"log","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblog-3242a8c3b3d72769.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.6.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand_core","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.6.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","getrandom","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand_core-aa1df72cb81e420e.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"num-traits","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libnum_traits-1c8d0251ac4ea61d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rayon 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/rayon-4f0513187044c0f2/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"escargot 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/escargot-44a89e78cfd0d26f/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"yansi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/yansi-0.5.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"yansi","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/yansi-0.5.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libyansi-62809433fc3ff8e9.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-automata-0.1.10/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"regex-automata","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-automata-0.1.10/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libregex_automata-741e79c4b6938d7d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/remove_dir_all-0.5.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"remove_dir_all","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/remove_dir_all-0.5.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libremove_dir_all-bcafaf4f00d8e4a4.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"regex-syntax 0.6.27 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-syntax-0.6.27/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"regex-syntax","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-syntax-0.6.27/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libregex_syntax-3fe0a07eb00f644f.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"ucd-trie 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ucd-trie","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libucd_trie-b01a07ea40c7e7ed.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"plotters-backend 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-backend-0.3.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"plotters-backend","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-backend-0.3.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libplotters_backend-34c8fa8c2b121eeb.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"ppv-lite86 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ppv-lite86-0.2.16/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ppv-lite86","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ppv-lite86-0.2.16/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["simd","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libppv_lite86-3b9c603c4b32aff6.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"fastrand 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fastrand-1.8.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"fastrand","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fastrand-1.8.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libfastrand-52ad6b37c39c8a90.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"itertools 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itertools","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","use_alloc","use_std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libitertools-ba05d1064477b941.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","derive","serde_derive","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde-e026f194fb97de03.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"thiserror","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libthiserror-4481f6326f711f93.rlib","/home/epage/src/personal/winnow/target/debug/deps/libthiserror-4481f6326f711f93.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"crossbeam-epoch 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-epoch","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_epoch-caa786e06425cee9.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"thiserror","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libthiserror-9187d79370422188.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rustix 0.36.8 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rustix","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","io-lifetimes","libc","std","termios","use-libc-auxv"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librustix-ec48622574cca747.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"crossbeam-channel 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-channel-0.5.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-channel","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-channel-0.5.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["crossbeam-utils","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_channel-a9b11ccef7773884.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"escargot 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/escargot-5711ac23b4781245/out"} +{"reason":"compiler-artifact","package_id":"tempfile 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tempfile-3.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"tempfile","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tempfile-3.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtempfile-8b83de608c2a0658.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rand_chacha 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_chacha-0.3.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand_chacha","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_chacha-0.3.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand_chacha-5967d0068d39b018.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"rayon 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["has_step_by_rev","has_min_const_generics","has_control_flow"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/rayon-d01aa17f59a1e98f/out"} +{"reason":"compiler-artifact","package_id":"plotters-svg 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-svg-0.3.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"plotters-svg","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-svg-0.3.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libplotters_svg-2bb014e4a5d863b1.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-core-0.1.10/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"csv-core","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-core-0.1.10/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcsv_core-bf47a6ecf716f212.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"textwrap","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtextwrap-1f9a5b633ba2872d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/atty-0.2.14/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"atty","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/atty-0.2.14/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libatty-7590cc9da5872c3b.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/wait-timeout-0.2.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"wait-timeout","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/wait-timeout-0.2.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libwait_timeout-d79299ed1c2df76b.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"diff 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/diff-0.1.13/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"diff","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/diff-0.1.13/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libdiff-a3c1327ada950886.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"concolor-query 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-query-0.1.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"concolor-query","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-query-0.1.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["windows"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libconcolor_query-1b1f33bda6da9636.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/same-file-1.0.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"same-file","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/same-file-1.0.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsame_file-8dba8c6e6bbcef0a.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"bit-vec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-vec-0.6.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bit-vec","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-vec-0.6.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbit_vec-5cc1be6dbbb7d172.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fnv-1.0.7/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"fnv","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fnv-1.0.7/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libfnv-e811c8615aede027.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde_json 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde_json","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde_json-10d232e85191c379.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"pest 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std","thiserror"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest-c28124a2ace8ce41.rlib","/home/epage/src/personal/winnow/target/debug/deps/libpest-c28124a2ace8ce41.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"crossbeam-deque 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-deque-0.8.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-deque","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-deque-0.8.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["crossbeam-epoch","crossbeam-utils","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_deque-a531937c1e514cf3.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"bstr 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bstr-0.2.17/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bstr","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bstr-0.2.17/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","lazy_static","regex-automata","serde","serde1","serde1-nostd","std","unicode"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbstr-3245cbc7e1b4eee0.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"is-terminal 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/is-terminal-0.4.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"is-terminal","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/is-terminal-0.4.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libis_terminal-ddb4d6f1cea88415.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"pest 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std","thiserror"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest-3fe2406158c36e91.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/doc-comment-476a3be5ae9523a0/build-script-build"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"cast 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cast-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"cast","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cast-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcast-0f6ca0f808d89c22.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"half 1.8.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/half-1.8.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"half","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/half-1.8.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libhalf-ec0164f76c2e0030.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-1.2.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quick-error","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-1.2.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquick_error-e7675fcf1fdb276d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"once_cell 1.15.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"once_cell","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","race","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libonce_cell-6a83ca81f3790104.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"itoa 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-0.4.8/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-0.4.8/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libitoa-af0357b7d39004b0.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"plotters 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-0.3.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"plotters","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-0.3.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["area_series","line_series","plotters-svg","svg_backend"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libplotters-c6ddec411d8166e5.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"walkdir 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/walkdir-2.3.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"walkdir","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/walkdir-2.3.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libwalkdir-9e7e41a0b2bf038d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"bit-set 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-set-0.5.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bit-set","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-set-0.5.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbit_set-2aef4909cbe7f4dc.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"pretty_assertions 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pretty_assertions-1.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pretty_assertions","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pretty_assertions-1.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpretty_assertions-917f731810bb748b.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-2.34.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"clap","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-2.34.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libclap-af397f0f338fc8b9.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","getrandom","libc","rand_chacha","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand-79231f927b479fcb.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"quick-xml 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-xml-0.23.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quick-xml","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-xml-0.23.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquick_xml-56f1f63175d80ec8.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rand_xorshift 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_xorshift-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand_xorshift","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_xorshift-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand_xorshift-52cda07a05fef0b5.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"pest_meta 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_meta-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest_meta","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_meta-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest_meta-e592b8a19ab895e0.rlib","/home/epage/src/personal/winnow/target/debug/deps/libpest_meta-e592b8a19ab895e0.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rayon-core 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rayon-core","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librayon_core-28c7fd349d6eb8a3.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"tinytemplate 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tinytemplate-1.2.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"tinytemplate","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tinytemplate-1.2.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtinytemplate-1e34f9b3f54388b1.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"serde_cbor 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_cbor-0.11.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde_cbor","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_cbor-0.11.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde_cbor-a6696a57f0dc1b3e.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"escargot 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"escargot","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libescargot-8a922c953f0ce0d3.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"csv 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-1.1.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"csv","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-1.1.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcsv-66898c2026240d6e.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rusty-fork 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rusty-fork-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rusty-fork","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rusty-fork-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["timeout","wait-timeout"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librusty_fork-3ff190906b9b0c88.rmeta"],"executable":null,"fresh":true} +{"reason":"build-script-executed","package_id":"doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/doc-comment-9f27b6aeba0913e7/out"} +{"reason":"compiler-artifact","package_id":"criterion-plot 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-plot-0.4.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"criterion-plot","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-plot-0.4.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcriterion_plot-dc7b9242779f53d6.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"concolor 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-0.0.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"concolor","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-0.0.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["auto","bitflags","clicolor","concolor-query","core","interactive","no_color","std","term","windows"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libconcolor-c2d97e9aa66666d7.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"regex 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-1.6.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"regex","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-1.6.0/src/lib.rs","edition":"2018","doc":true,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libregex-14a9fe50552aac49.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"os_pipe 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/os_pipe-1.1.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"os_pipe","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/os_pipe-1.1.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libos_pipe-be2a22288f932a49.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"quick-error 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-2.0.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quick-error","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-2.0.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquick_error-280edfc32cc83812.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"normalize-line-endings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/normalize-line-endings-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"normalize-line-endings","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/normalize-line-endings-0.3.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libnormalize_line_endings-8a66ba357a389996.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bytecount-0.6.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bytecount","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bytecount-0.6.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbytecount-c083e2cdbbb4f003.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"snapbox-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-macros-0.3.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"snapbox-macros","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-macros-0.3.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsnapbox_macros-d9da77d55f4279bc.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"oorandom 11.1.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/oorandom-11.1.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"oorandom","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/oorandom-11.1.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liboorandom-052fb0be6ac16c94.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"byteorder 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"byteorder","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbyteorder-9f6a3ecb302657b0.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"similar 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/similar-2.2.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"similar","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/similar-2.2.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","inline","text"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsimilar-ca2082771c207a3c.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/termcolor-1.2.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"termcolor","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/termcolor-1.2.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtermcolor-dad1a04bc5d2f742.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"pest_generator 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_generator-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest_generator","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_generator-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest_generator-17d62a3fe28e46f5.rlib","/home/epage/src/personal/winnow/target/debug/deps/libpest_generator-17d62a3fe28e46f5.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"rayon 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rayon","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librayon-41dd0a10f9292557.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"proptest 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proptest-1.0.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"proptest","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proptest-1.0.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["bit-set","break-dead-code","default","fork","lazy_static","quick-error","regex-syntax","rusty-fork","std","tempfile","timeout"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libproptest-7f94966e4936da95.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"snapbox 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-0.4.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"snapbox","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-0.4.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["color","color-auto","concolor","default","diff","examples"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsnapbox-d99468a5201f9b87.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"doc_comment","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libdoc_comment-5644793091a6953d.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"lexopt 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lexopt-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"lexopt","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lexopt-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblexopt-c945e030a97b8e1f.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"circular 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/circular-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"circular","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/circular-0.3.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcircular-6190d46717d189a0.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"winnow","src_path":"/home/epage/src/personal/winnow/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libwinnow-a64b99fd45b2e97c.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"pest_derive 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_derive-2.5.5/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"pest_derive","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_derive-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest_derive-23acec6b80f586aa.so"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"criterion 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-0.3.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"criterion","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-0.3.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["cargo_bench_support","default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcriterion-864d2d30e85a25cf.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"handlebars 4.3.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-4.3.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"handlebars","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-4.3.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libhandlebars-8f7ca769e2915c7a.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"term-transcript 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/term-transcript-0.2.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"term-transcript","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/term-transcript-0.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["atty","default","handlebars","pretty_assertions","quick-xml","serde","svg","test"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libterm_transcript-67537cf74f0568cc.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"ini","src_path":"/home/epage/src/personal/winnow/examples/ini/main.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libini-029ff669d11d054a.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"custom_error","src_path":"/home/epage/src/personal/winnow/examples/custom_error.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libcustom_error-8382fb7d49937909.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"s_expression","src_path":"/home/epage/src/personal/winnow/examples/s_expression/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libs_expression-0161e16eb3795e0c.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"css","src_path":"/home/epage/src/personal/winnow/examples/css/main.rs","edition":"2021","doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libcss-d003331b0eaf24a5.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"json","src_path":"/home/epage/src/personal/winnow/examples/json/main.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libjson-20da0c01e9db35aa.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"ndjson","src_path":"/home/epage/src/personal/winnow/examples/ndjson/main.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libndjson-17afbe6b158251ab.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"http","src_path":"/home/epage/src/personal/winnow/examples/http/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libhttp-08613cd431f59551.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"iterator","src_path":"/home/epage/src/personal/winnow/examples/iterator.rs","edition":"2021","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libiterator-46a33f71a5378497.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"string","src_path":"/home/epage/src/personal/winnow/examples/string/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libstring-73fadc9999eff689.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"arithmetic","src_path":"/home/epage/src/personal/winnow/examples/arithmetic/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libarithmetic-04ca5fb45c28ebc2.rmeta"],"executable":null,"fresh":true} +{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"json_iterator","src_path":"/home/epage/src/personal/winnow/examples/json_iterator.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libjson_iterator-9bb330a957b0d53d.rmeta"],"executable":null,"fresh":true} +{"reason":"build-finished","success":true} + diff --git a/vendor/winnow/examples/ndjson/main.rs b/vendor/winnow/examples/ndjson/main.rs new file mode 100644 index 0000000..6b2a716 --- /dev/null +++ b/vendor/winnow/examples/ndjson/main.rs @@ -0,0 +1,114 @@ +mod parser; + +use std::io::Read; + +use winnow::error::ErrMode; +use winnow::error::InputError; +use winnow::error::Needed; +use winnow::prelude::*; +use winnow::stream::Offset; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + let input = args.input.ok_or_else(|| lexopt::Error::MissingValue { + option: Some("<PATH>".to_owned()), + })?; + + 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; + let min_buffer_growth = 100; + let buffer_growth_factor = 2; + let mut buffer = circular::Buffer::with_capacity(buffer_size); + loop { + let read = file.read(buffer.space()).map_err(to_lexopt)?; + eprintln!("read {}", read); + if read == 0 { + // Should be EOF since we always make sure there is `available_space` + assert_ne!(buffer.available_space(), 0); + assert_eq!( + buffer.available_data(), + 0, + "leftover data: {}", + String::from_utf8_lossy(buffer.data()) + ); + break; + } + 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); + println!(); + // Tell the buffer how much we read + let consumed = remainder.offset_from(&input); + buffer.consume(consumed); + } + Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => { + return Err(fmt_lexopt(e.to_string())); + } + Err(ErrMode::Incomplete(Needed::Size(size))) => { + // Without the format telling us how much space is required, we really should + // treat this the same as `Unknown` but are doing this to demonstrate how to + // handle `Size`. + // + // Even when the format has a header to tell us `Size`, we could hit incidental + // `Size(1)`s, so make sure we buffer more space than that to avoid reading + // 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); + buffer.grow(new_capacity); + if buffer.available_space() < head_room { + eprintln!("buffer shift"); + buffer.shift(); + } + break; + } + Err(ErrMode::Incomplete(Needed::Unknown)) => { + let new_capacity = buffer_growth_factor * buffer.capacity(); + eprintln!("growing buffer to {}", new_capacity); + buffer.grow(new_capacity); + break; + } + } + } + } + + Ok(()) +} + +#[derive(Default)] +struct Args { + input: Option<std::path::PathBuf>, +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Value(input) => { + res.input = Some(input.into()); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} + +fn to_lexopt(e: impl std::error::Error + Send + Sync + 'static) -> lexopt::Error { + lexopt::Error::Custom(Box::new(e)) +} + +fn fmt_lexopt(e: String) -> lexopt::Error { + lexopt::Error::Custom(e.into()) +} diff --git a/vendor/winnow/examples/ndjson/parser.rs b/vendor/winnow/examples/ndjson/parser.rs new file mode 100644 index 0000000..aaa5c93 --- /dev/null +++ b/vendor/winnow/examples/ndjson/parser.rs @@ -0,0 +1,338 @@ +use std::collections::HashMap; +use std::str; + +use winnow::prelude::*; +use winnow::{ + ascii::float, + ascii::line_ending, + combinator::alt, + combinator::cut_err, + combinator::{delimited, preceded, separated_pair, terminated}, + combinator::{fold_repeat, separated0}, + error::{AddContext, ParserError}, + stream::Partial, + token::{any, none_of, take, take_while}, +}; + +#[derive(Debug, PartialEq, Clone)] +pub enum JsonValue { + Null, + Boolean(bool), + Str(String), + Num(f64), + Array(Vec<JsonValue>), + Object(HashMap<String, JsonValue>), +} + +/// Use `Partial` to cause `ErrMode::Incomplete` while parsing +pub type Stream<'i> = Partial<&'i str>; + +pub fn ndjson<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<Option<JsonValue>, E> { + alt(( + terminated(delimited(ws, json_value, ws), line_ending).map(Some), + line_ending.value(None), + )) + .parse_next(input) +} + +// --Besides `WS`, same as a regular json parser ---------------------------- + +/// `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>>( + input: &mut Stream<'i>, +) -> PResult<JsonValue, E> { + // `alt` combines the each value parser. It returns the result of the first + // successful parser, or an error + alt(( + null.value(JsonValue::Null), + boolean.map(JsonValue::Boolean), + string.map(JsonValue::Str), + float.map(JsonValue::Num), + array.map(JsonValue::Array), + object.map(JsonValue::Object), + )) + .parse_next(input) +} + +/// `tag(string)` generates a parser that recognizes 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> { + // This is a parser that returns `"null"` if it sees the string "null", and + // an error otherwise + "null".parse_next(input) +} + +/// We can combine `tag` with other functions, like `value` which returns a given constant value on +/// success. +fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> { + // This is a parser that returns `true` if it sees the string "true", and + // an error otherwise + let parse_true = "true".value(true); + + // This is a parser that returns `false` if it sees the string "false", and + // an error otherwise + let parse_false = "false".value(false); + + 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 +/// character, before the string (using `preceded`) and after the string (using `terminated`). +fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<String, E> { + preceded( + '\"', + // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to + // combinators like `alt` that they should not try other parsers. We were in the + // right branch (since we found the `"` character) but encountered an error when + // parsing the string + cut_err(terminated( + fold_repeat(0.., character, String::new, |mut string, c| { + string.push(c); + string + }), + '\"', + )), + ) + // `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") + .parse_next(input) +} + +/// You can mix the above declarative parsing with an imperative style to handle more unique cases, +/// like escaping +fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + let c = none_of('"').parse_next(input)?; + if c == '\\' { + alt(( + any.verify_map(|c| { + Some(match c { + '"' | '\\' | '/' => c, + 'b' => '\x08', + 'f' => '\x0C', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return None, + }) + }), + preceded('u', unicode_escape), + )) + .parse_next(input) + } else { + Ok(c) + } +} + +fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> { + alt(( + // Not a surrogate + u16_hex + .verify(|cp| !(0xD800..0xE000).contains(cp)) + .map(|cp| cp as u32), + // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details + separated_pair(u16_hex, "\\u", u16_hex) + .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low)) + .map(|(high, low)| { + let high_ten = (high as u32) - 0xD800; + let low_ten = (low as u32) - 0xDC00; + (high_ten << 10) + low_ten + 0x10000 + }), + )) + .verify_map( + // Could be probably replaced with .unwrap() or _unchecked due to the verify checks + std::char::from_u32, + ) + .parse_next(input) +} + +fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> { + take(4usize) + .verify_map(|s| u16::from_str_radix(s, 16).ok()) + .parse_next(input) +} + +/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly, +/// 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>>( + input: &mut Stream<'i>, +) -> PResult<Vec<JsonValue>, E> { + preceded( + ('[', ws), + cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), + ) + .context("array") + .parse_next(input) +} + +fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<HashMap<String, JsonValue>, E> { + preceded( + ('{', ws), + cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), + ) + .context("object") + .parse_next(input) +} + +fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>( + input: &mut Stream<'i>, +) -> PResult<(String, JsonValue), E> { + separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) +} + +/// Parser combinators are constructed from the bottom up: +/// first we write parsers for the smallest elements (here a space character), +/// then we'll combine them in larger parsers +fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { + // Combinators like `take_while` return a function. That function is the + // parser,to which we can pass the input + take_while(0.., WS).parse_next(input) +} + +const WS: &[char] = &[' ', '\t']; + +#[cfg(test)] +mod test { + #[allow(clippy::useless_attribute)] + #[allow(dead_code)] // 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>>; + + #[test] + fn json_string() { + assert_eq!( + string::<Error<'_>>.parse_peek(Partial::new("\"\"")), + Ok((Partial::new(""), "".to_string())) + ); + assert_eq!( + string::<Error<'_>>.parse_peek(Partial::new("\"abc\"")), + Ok((Partial::new(""), "abc".to_string())) + ); + assert_eq!( + 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() + )), + ); + assert_eq!( + string::<Error<'_>>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")), + Ok((Partial::new(""), "😐".to_string())) + ); + + 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<'_>> + .parse_peek(Partial::new("\"\\uD800\"")) + .is_err()); + assert!(string::<Error<'_>> + .parse_peek(Partial::new("\"\\uD800\\uD800\"")) + .is_err()); + assert!(string::<Error<'_>> + .parse_peek(Partial::new("\"\\uDC00\"")) + .is_err()); + } + + #[test] + fn json_object() { + use JsonValue::{Num, Object, Str}; + + let input = r#"{"a":42,"b":"x"} +"#; + + let expected = Object( + vec![ + ("a".to_string(), Num(42.0)), + ("b".to_string(), Str("x".to_string())), + ] + .into_iter() + .collect(), + ); + + assert_eq!( + ndjson::<Error<'_>>.parse_peek(Partial::new(input)), + Ok((Partial::new(""), Some(expected))) + ); + } + + #[test] + fn json_array() { + use JsonValue::{Array, Num, Str}; + + let input = r#"[42,"x"] +"#; + + let expected = Array(vec![Num(42.0), Str("x".to_string())]); + + assert_eq!( + ndjson::<Error<'_>>.parse_peek(Partial::new(input)), + Ok((Partial::new(""), Some(expected))) + ); + } + + #[test] + fn json_whitespace() { + use JsonValue::{Array, Boolean, Null, Num, Object, Str}; + + let input = r#" { "null" : null, "true" :true , "false": false , "number" : 123e4 , "string" : " abc 123 " , "array" : [ false , 1 , "two" ] , "object" : { "a" : 1.0 , "b" : "c" } , "empty_array" : [ ] , "empty_object" : { } } +"#; + + assert_eq!( + 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())), + ( + "array".to_string(), + Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) + ), + ( + "object".to_string(), + Object( + vec![ + ("a".to_string(), Num(1.0)), + ("b".to_string(), Str("c".to_string())), + ] + .into_iter() + .collect() + ) + ), + ("empty_array".to_string(), Array(vec![]),), + ("empty_object".to_string(), Object(HashMap::new()),), + ] + .into_iter() + .collect() + )) + )) + ); + } +} diff --git a/vendor/winnow/examples/s_expression/main.rs b/vendor/winnow/examples/s_expression/main.rs new file mode 100644 index 0000000..da055be --- /dev/null +++ b/vendor/winnow/examples/s_expression/main.rs @@ -0,0 +1,20 @@ +//! In this example we build an [S-expression](https://en.wikipedia.org/wiki/S-expression) +//! parser and tiny [lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)) interpreter. +//! Lisp is a simple type of language made up of Atoms and Lists, forming easily parsable trees. + +#![cfg(feature = "alloc")] + +mod parser; + +fn main() { + let expression_1 = "((if (= (+ 3 (/ 9 3)) + (* 2 3)) + * + /) + 456 123)"; + println!( + "\"{}\"\nevaled gives us: {:?}", + expression_1, + parser::eval_from_str(expression_1) + ); +} diff --git a/vendor/winnow/examples/s_expression/parser.rs b/vendor/winnow/examples/s_expression/parser.rs new file mode 100644 index 0000000..919dcf4 --- /dev/null +++ b/vendor/winnow/examples/s_expression/parser.rs @@ -0,0 +1,361 @@ +//! In this example we build an [S-expression](https://en.wikipedia.org/wiki/S-expression) +//! parser and tiny [lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)) interpreter. +//! Lisp is a simple type of language made up of Atoms and Lists, forming easily parsable trees. + +use winnow::{ + ascii::{alpha1, digit1, multispace0, multispace1}, + combinator::alt, + combinator::repeat, + combinator::{cut_err, opt}, + combinator::{delimited, preceded, terminated}, + error::ContextError, + error::StrContext, + prelude::*, + token::one_of, +}; + +/// 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> { + parse_expr + .parse(src) + .map_err(|e| e.to_string()) + .and_then(|exp| eval_expression(exp).ok_or_else(|| "Eval failed".to_string())) +} + +/// 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: +/// ``` +/// Expr::Quote(vec![Expr::Constant(Atom::Num(1)), +/// Expr::Constant(Atom::Num(2)), +/// Expr::Constant(Atom::Num(3))]) +/// Quote takes an S-expression and prevents evaluation of it, making it a data +/// 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 { + Constant(Atom), + /// (func-name arg1 arg2) + Application(Box<Expr>, Vec<Expr>), + /// (if predicate do-this) + If(Box<Expr>, Box<Expr>), + /// (if predicate do-this otherwise-do-this) + IfElse(Box<Expr>, Box<Expr>, Box<Expr>), + /// '(3 (if (+ 3 3) 4 5) 7) + Quote(Vec<Expr>), +} + +/// 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 { + Num(i32), + Keyword(String), + Boolean(bool), + BuiltIn(BuiltIn), +} + +/// Now, the most basic type. We define some built-in functions that our lisp has +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum BuiltIn { + Plus, + Minus, + Times, + Divide, + Equal, + Not, +} + +/// With types defined, we move onto the top-level expression parser! +fn parse_expr(i: &mut &'_ str) -> PResult<Expr> { + preceded( + multispace0, + alt((parse_constant, parse_application, parse_if, parse_quote)), + ) + .parse_next(i) +} + +/// We then add the Expr layer on top +fn parse_constant(i: &mut &'_ str) -> PResult<Expr> { + parse_atom.map(Expr::Constant).parse_next(i) +} + +/// Now we take all these simple parsers and connect them. +/// We can now parse half of our language! +fn parse_atom(i: &mut &'_ str) -> PResult<Atom> { + alt(( + parse_num, + parse_bool, + parse_builtin.map(Atom::BuiltIn), + parse_keyword, + )) + .parse_next(i) +} + +/// Next up is number parsing. We're keeping it simple here by accepting any number (> 1) +/// of digits but ending the program if it doesn't fit into an i32. +fn parse_num(i: &mut &'_ str) -> PResult<Atom> { + alt(( + digit1.try_map(|digit_str: &str| digit_str.parse::<i32>().map(Atom::Num)), + preceded("-", digit1).map(|digit_str: &str| Atom::Num(-digit_str.parse::<i32>().unwrap())), + )) + .parse_next(i) +} + +/// Our boolean values are also constant, so we can do it the same way +fn parse_bool(i: &mut &'_ str) -> PResult<Atom> { + alt(( + "#t".map(|_| Atom::Boolean(true)), + "#f".map(|_| Atom::Boolean(false)), + )) + .parse_next(i) +} + +fn parse_builtin(i: &mut &'_ str) -> PResult<BuiltIn> { + // alt gives us the result of first parser that succeeds, of the series of + // parsers we give it + alt(( + parse_builtin_op, + // map lets us process the parsed output, in this case we know what we parsed, + // so we ignore the input and return the BuiltIn directly + "not".map(|_| BuiltIn::Not), + )) + .parse_next(i) +} + +/// Continuing the trend of starting from the simplest piece and building up, +/// we start by creating a parser for the built-in operator functions. +fn parse_builtin_op(i: &mut &'_ str) -> PResult<BuiltIn> { + // one_of matches one of the characters we give it + let t = one_of(['+', '-', '*', '/', '=']).parse_next(i)?; + + // because we are matching single character tokens, we can do the matching logic + // on the returned value + Ok(match t { + '+' => BuiltIn::Plus, + '-' => BuiltIn::Minus, + '*' => BuiltIn::Times, + '/' => BuiltIn::Divide, + '=' => BuiltIn::Equal, + _ => unreachable!(), + }) +} + +/// The next easiest thing to parse are keywords. +/// We introduce some error handling combinators: `context` for human readable errors +/// and `cut_err` to prevent back-tracking. +/// +/// Put plainly: `preceded(":", cut_err(alpha1))` means that once we see the `:` +/// character, we have to see one or more alphabetic characters or the input is invalid. +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())) + .parse_next(i) +} + +/// We can now use our new combinator to define the rest of the `Expr`s. +/// +/// Starting with function application, we can see how the parser mirrors our data +/// definitions: our definition is `Application(Box<Expr>, Vec<Expr>)`, so we know +/// that we need to parse an expression and then parse 0 or more expressions, all +/// wrapped in an S-expression. +/// +/// tuples are themselves a parser, used to sequence parsers together, so we can translate this +/// directly and then map over it to transform the output into an `Expr::Application` +fn parse_application(i: &mut &'_ str) -> PResult<Expr> { + let application_inner = (parse_expr, repeat(0.., parse_expr)) + .map(|(head, tail)| Expr::Application(Box::new(head), tail)); + // finally, we wrap it in an s-expression + s_exp(application_inner).parse_next(i) +} + +/// Because `Expr::If` and `Expr::IfElse` are so similar (we easily could have +/// defined `Expr::If` to have an `Option` for the else block), we parse both +/// in a single function. +/// +/// In fact, we define our parser as if `Expr::If` was defined with an Option in it, +/// we have the `opt` combinator which fits very nicely here. +fn parse_if(i: &mut &'_ str) -> PResult<Expr> { + let if_inner = preceded( + // here to avoid ambiguity with other names starting with `if`, if we added + // variables to our language, we say that if must be terminated by at least + // one whitespace character + terminated("if", multispace1), + cut_err((parse_expr, parse_expr, opt(parse_expr))), + ) + .map(|(pred, true_branch, maybe_false_branch)| { + if let Some(false_branch) = maybe_false_branch { + Expr::IfElse( + Box::new(pred), + Box::new(true_branch), + Box::new(false_branch), + ) + } else { + Expr::If(Box::new(pred), Box::new(true_branch)) + } + }) + .context(StrContext::Label("if expression")); + s_exp(if_inner).parse_next(i) +} + +/// A quoted S-expression is list data structure. +/// +/// This example doesn't have the symbol atom, but by adding variables and changing +/// the definition of quote to not always be around an S-expression, we'd get them +/// naturally. +fn parse_quote(i: &mut &'_ str) -> PResult<Expr> { + // this should look very straight-forward after all we've done: + // we find the `'` (quote) character, use cut_err to say that we're unambiguously + // looking for an s-expression of 0 or more expressions, and then parse them + preceded("'", cut_err(s_exp(repeat(0.., parse_expr)))) + .context(StrContext::Label("quote")) + .map(Expr::Quote) + .parse_next(i) +} + +/// Before continuing, we need a helper function to parse lists. +/// A list starts with `(` and ends with a matching `)`. +/// By putting whitespace and newline parsing here, we can avoid having to worry about it +/// in much of the rest of the parser. +//.parse_next/ +/// Unlike the previous functions, this function doesn't take or consume input, instead it +/// takes a parsing function and returns a new parsing function. +fn s_exp<'a, O1, F>(inner: F) -> impl Parser<&'a str, O1, ContextError> +where + F: Parser<&'a str, O1, ContextError>, +{ + delimited( + '(', + preceded(multispace0, inner), + cut_err(preceded(multispace0, ')')).context(StrContext::Label("closing paren")), + ) +} + +/// And that's it! +/// We can now parse our entire lisp language. +/// +/// But in order to make it a little more interesting, we can hack together +/// 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 +fn eval_expression(e: Expr) -> Option<Expr> { + match e { + // Constants and quoted s-expressions are our base-case + Expr::Constant(_) | Expr::Quote(_) => Some(e), + // we then recursively `eval_expression` in the context of our special forms + // and built-in operators + Expr::If(pred, true_branch) => { + let reduce_pred = eval_expression(*pred)?; + if get_bool_from_expr(reduce_pred)? { + eval_expression(*true_branch) + } else { + None + } + } + Expr::IfElse(pred, true_branch, false_branch) => { + let reduce_pred = eval_expression(*pred)?; + if get_bool_from_expr(reduce_pred)? { + eval_expression(*true_branch) + } else { + eval_expression(*false_branch) + } + } + Expr::Application(head, tail) => { + let reduced_head = eval_expression(*head)?; + let reduced_tail = tail + .into_iter() + .map(eval_expression) + .collect::<Option<Vec<Expr>>>()?; + if let Expr::Constant(Atom::BuiltIn(bi)) = reduced_head { + Some(Expr::Constant(match bi { + BuiltIn::Plus => Atom::Num( + reduced_tail + .into_iter() + .map(get_num_from_expr) + .collect::<Option<Vec<i32>>>()? + .into_iter() + .sum(), + ), + BuiltIn::Times => Atom::Num( + reduced_tail + .into_iter() + .map(get_num_from_expr) + .collect::<Option<Vec<i32>>>()? + .into_iter() + .product(), + ), + BuiltIn::Equal => Atom::Boolean( + reduced_tail + .iter() + .zip(reduced_tail.iter().skip(1)) + .all(|(a, b)| a == b), + ), + BuiltIn::Not => { + if reduced_tail.len() != 1 { + return None; + } else { + Atom::Boolean(!get_bool_from_expr( + reduced_tail.first().cloned().unwrap(), + )?) + } + } + BuiltIn::Minus => { + Atom::Num(if let Some(first_elem) = reduced_tail.first().cloned() { + let fe = get_num_from_expr(first_elem)?; + reduced_tail + .into_iter() + .map(get_num_from_expr) + .collect::<Option<Vec<i32>>>()? + .into_iter() + .skip(1) + .fold(fe, |a, b| a - b) + } else { + Default::default() + }) + } + BuiltIn::Divide => { + Atom::Num(if let Some(first_elem) = reduced_tail.first().cloned() { + let fe = get_num_from_expr(first_elem)?; + reduced_tail + .into_iter() + .map(get_num_from_expr) + .collect::<Option<Vec<i32>>>()? + .into_iter() + .skip(1) + .fold(fe, |a, b| a / b) + } else { + Default::default() + }) + } + })) + } else { + None + } + } + } +} + +/// To start we define a couple of helper functions +fn get_num_from_expr(e: Expr) -> Option<i32> { + if let Expr::Constant(Atom::Num(n)) = e { + Some(n) + } else { + None + } +} + +fn get_bool_from_expr(e: Expr) -> Option<bool> { + if let Expr::Constant(Atom::Boolean(b)) = e { + Some(b) + } else { + None + } +} diff --git a/vendor/winnow/examples/string/main.rs b/vendor/winnow/examples/string/main.rs new file mode 100644 index 0000000..0c2647e --- /dev/null +++ b/vendor/winnow/examples/string/main.rs @@ -0,0 +1,70 @@ +//! This example shows an example of how to parse an escaped string. The +//! rules for the string are similar to JSON and rust. A string is: +//! +//! - Enclosed by double quotes +//! - Can contain any raw unescaped code point besides \ and " +//! - Matches the following escape sequences: \b, \f, \n, \r, \t, \", \\, \/ +//! - Matches code points like Rust: \u{XXXX}, where XXXX can be up to 6 +//! hex characters +//! - an escape followed by whitespace consumes all whitespace between the +//! escape and the next non-whitespace character + +#![cfg(feature = "alloc")] + +mod parser; + +use winnow::prelude::*; + +fn main() -> Result<(), lexopt::Error> { + let args = Args::parse()?; + + 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(()) +} + +#[derive(Default)] +struct Args { + input: Option<String>, +} + +impl Args { + fn parse() -> Result<Self, lexopt::Error> { + use lexopt::prelude::*; + + let mut res = Args::default(); + + let mut args = lexopt::Parser::from_env(); + while let Some(arg) = args.next()? { + match arg { + Value(input) => { + res.input = Some(input.string()?); + } + _ => return Err(arg.unexpected()), + } + } + Ok(res) + } +} + +#[test] +fn simple() { + let data = "\"abc\""; + let result = parser::parse_string::<()>.parse(data); + assert_eq!(result, Ok(String::from("abc"))); +} + +#[test] +fn escaped() { + let data = "\"tab:\\tafter tab, newline:\\nnew line, quote: \\\", emoji: \\u{1F602}, newline:\\nescaped whitespace: \\ abc\""; + let result = parser::parse_string::<()>.parse(data); + assert_eq!( + result, + Ok(String::from("tab:\tafter tab, newline:\nnew line, quote: \", emoji: 😂, newline:\nescaped whitespace: abc")) + ); +} diff --git a/vendor/winnow/examples/string/parser.rs b/vendor/winnow/examples/string/parser.rs new file mode 100644 index 0000000..6b63458 --- /dev/null +++ b/vendor/winnow/examples/string/parser.rs @@ -0,0 +1,165 @@ +//! This example shows an example of how to parse an escaped string. The +//! rules for the string are similar to JSON and rust. A string is: +//! +//! - Enclosed by double quotes +//! - Can contain any raw unescaped code point besides \ and " +//! - Matches the following escape sequences: \b, \f, \n, \r, \t, \", \\, \/ +//! - Matches code points like Rust: \u{XXXX}, where XXXX can be up to 6 +//! hex characters +//! - an escape followed by whitespace consumes all whitespace between the +//! escape and the next non-whitespace character + +use winnow::ascii::multispace1; +use winnow::combinator::alt; +use winnow::combinator::fold_repeat; +use winnow::combinator::{delimited, preceded}; +use winnow::error::{FromExternalError, ParserError}; +use winnow::prelude::*; +use winnow::token::{take_till1, take_while}; + +/// 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> +where + E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, +{ + // fold_repeat is the equivalent of iterator::fold. It runs a parser in a loop, + // and for each output value, calls a folding function on each output value. + let build_string = fold_repeat( + 0.., + // Our parser function – parses a single string fragment + parse_fragment, + // Our init value, an empty string + String::new, + // Our folding function. For each fragment, append the fragment to the + // string. + |mut string, fragment| { + match fragment { + StringFragment::Literal(s) => string.push_str(s), + StringFragment::EscapedChar(c) => string.push(c), + StringFragment::EscapedWS => {} + } + string + }, + ); + + // Finally, parse the string. Note that, if `build_string` could accept a raw + // " character, the closing delimiter " would never match. When using + // `delimited` with a looping parser (like fold_repeat), be sure that the + // loop won't accidentally match your closing delimiter! + delimited('"', build_string, '"').parse_next(input) +} + +/// A string fragment contains a fragment of a string being parsed: either +/// a non-empty Literal (a series of non-escaped characters), a single +/// parsed escaped character, or a block of escaped whitespace. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum StringFragment<'a> { + Literal(&'a str), + EscapedChar(char), + EscapedWS, +} + +/// Combine `parse_literal`, `parse_escaped_whitespace`, and `parse_escaped_char` +/// into a `StringFragment`. +fn parse_fragment<'a, E>(input: &mut &'a str) -> PResult<StringFragment<'a>, E> +where + E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, +{ + alt(( + // The `map` combinator runs a parser, then applies a function to the output + // of that parser. + parse_literal.map(StringFragment::Literal), + parse_escaped_char.map(StringFragment::EscapedChar), + parse_escaped_whitespace.value(StringFragment::EscapedWS), + )) + .parse_next(input) +} + +/// Parse a non-empty block of text that doesn't include \ or " +fn parse_literal<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<&'a str, E> { + // `take_till1` parses a string of 0 or more characters that aren't one of the + // given characters. + let not_quote_slash = take_till1(['"', '\\']); + + // `verify` runs a parser, then runs a verification function on the output of + // the parser. The verification function accepts the output only if it + // returns true. In this case, we want to ensure that the output of take_till1 + // is non-empty. + not_quote_slash + .verify(|s: &str| !s.is_empty()) + .parse_next(input) +} + +// parser combinators are constructed from the bottom up: +// first we write parsers for the smallest elements (escaped characters), +// then combine them into larger parsers. + +/// Parse an escaped character: \n, \t, \r, \u{00AC}, etc. +fn parse_escaped_char<'a, E>(input: &mut &'a str) -> PResult<char, E> +where + E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, +{ + preceded( + '\\', + // `alt` tries each parser in sequence, returning the result of + // the first successful match + alt(( + parse_unicode, + // The `value` parser returns a fixed value (the first argument) if its + // parser (the second argument) succeeds. In these cases, it looks for + // the marker characters (n, r, t, etc) and returns the matching + // character (\n, \r, \t, etc). + 'n'.value('\n'), + 'r'.value('\r'), + 't'.value('\t'), + 'b'.value('\u{08}'), + 'f'.value('\u{0C}'), + '\\'.value('\\'), + '/'.value('/'), + '"'.value('"'), + )), + ) + .parse_next(input) +} + +/// Parse a unicode sequence, of the form u{XXXX}, where XXXX is 1 to 6 +/// hexadecimal numerals. We will combine this later with `parse_escaped_char` +/// to parse sequences like \u{00AC}. +fn parse_unicode<'a, E>(input: &mut &'a str) -> PResult<char, E> +where + E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, +{ + // `take_while` parses between `m` and `n` bytes (inclusive) that match + // a predicate. `parse_hex` here parses between 1 and 6 hexadecimal numerals. + let parse_hex = take_while(1..=6, |c: char| c.is_ascii_hexdigit()); + + // `preceded` takes a prefix parser, and if it succeeds, returns the result + // of the body parser. In this case, it parses u{XXXX}. + let parse_delimited_hex = preceded( + 'u', + // `delimited` is like `preceded`, but it parses both a prefix and a suffix. + // It returns the result of the middle parser. In this case, it parses + // {XXXX}, where XXXX is 1 to 6 hex numerals, and returns XXXX + delimited('{', parse_hex, '}'), + ); + + // `try_map` takes the result of a parser and applies a function that returns + // a Result. In this case we take the hex bytes from parse_hex and attempt to + // convert them to a u32. + let parse_u32 = parse_delimited_hex.try_map(move |hex| u32::from_str_radix(hex, 16)); + + // verify_map is like try_map, but it takes an Option instead of a Result. If + // the function returns None, verify_map returns an error. In this case, because + // not all u32 values are valid unicode code points, we have to fallibly + // convert to char with from_u32. + parse_u32.verify_map(std::char::from_u32).parse_next(input) +} + +/// Parse a backslash, followed by any amount of whitespace. This is used later +/// to discard any escaped whitespace. +fn parse_escaped_whitespace<'a, E: ParserError<&'a str>>( + input: &mut &'a str, +) -> PResult<&'a str, E> { + preceded('\\', multispace1).parse_next(input) +} diff --git a/vendor/winnow/src/_topic/arithmetic.rs b/vendor/winnow/src/_topic/arithmetic.rs new file mode 100644 index 0000000..d94b4fa --- /dev/null +++ b/vendor/winnow/src/_topic/arithmetic.rs @@ -0,0 +1,19 @@ +//! # Arithmetic +//! +//! ## Direct evaluation +//! +//! ```rust +#![doc = include_str!("../../examples/arithmetic/parser.rs")] +//! ``` +//! +//! ## Parse to AST +//! +//! ```rust +#![doc = include_str!("../../examples/arithmetic/parser_ast.rs")] +//! ``` +//! +//! ## Parse to Tokens then AST +//! +//! ```rust +#![doc = include_str!("../../examples/arithmetic/parser_lexer.rs")] +//! ``` diff --git a/vendor/winnow/src/_topic/error.rs b/vendor/winnow/src/_topic/error.rs new file mode 100644 index 0000000..c5374b4 --- /dev/null +++ b/vendor/winnow/src/_topic/error.rs @@ -0,0 +1,13 @@ +//! # Custom Errors +//! +//! The most basic error type is [`ParserError`][crate::error::ParserError] +//! +//! Optional traits include: +//! - [`AddContext`][crate::error::AddContext] +//! - [`FromExternalError`][crate::error::FromExternalError] +//! +//! # Example +//! +//!```rust +#![doc = include_str!("../../examples/custom_error.rs")] +//!``` diff --git a/vendor/winnow/src/_topic/fromstr.rs b/vendor/winnow/src/_topic/fromstr.rs new file mode 100644 index 0000000..d126d16 --- /dev/null +++ b/vendor/winnow/src/_topic/fromstr.rs @@ -0,0 +1,8 @@ +//! # Implementing `FromStr` +//! +//! The [`FromStr` trait][std::str::FromStr] provides +//! a common interface to parse from a string. +//! +//! ```rust +#![doc = include_str!("../../examples/css/parser.rs")] +//! ``` diff --git a/vendor/winnow/src/_topic/http.rs b/vendor/winnow/src/_topic/http.rs new file mode 100644 index 0000000..5001694 --- /dev/null +++ b/vendor/winnow/src/_topic/http.rs @@ -0,0 +1,5 @@ +//! # HTTP +//! +//! ```rust +#![doc = include_str!("../../examples/http/parser.rs")] +//! ``` diff --git a/vendor/winnow/src/_topic/ini.rs b/vendor/winnow/src/_topic/ini.rs new file mode 100644 index 0000000..b78ade5 --- /dev/null +++ b/vendor/winnow/src/_topic/ini.rs @@ -0,0 +1,5 @@ +//! # INI +//! +//! ```rust +#![doc = include_str!("../../examples/ini/parser.rs")] +//! ``` diff --git a/vendor/winnow/src/_topic/json.rs b/vendor/winnow/src/_topic/json.rs new file mode 100644 index 0000000..fb4df3b --- /dev/null +++ b/vendor/winnow/src/_topic/json.rs @@ -0,0 +1,5 @@ +//! # json +//! +//! ```rust,ignore +#![doc = include_str!("../../examples/json/parser.rs")] +//! ``` diff --git a/vendor/winnow/src/_topic/language.rs b/vendor/winnow/src/_topic/language.rs new file mode 100644 index 0000000..0cebc99 --- /dev/null +++ b/vendor/winnow/src/_topic/language.rs @@ -0,0 +1,330 @@ +//! # Elements of Programming Languages +//! +//! These are short recipes for accomplishing common tasks. +//! +//! * [Whitespace](#whitespace) +//! + [Wrapper combinators that eat whitespace before and after a parser](#wrapper-combinators-that-eat-whitespace-before-and-after-a-parser) +//! * [Comments](#comments) +//! + [`// C++/EOL-style comments`](#-ceol-style-comments) +//! + [`/* C-style comments */`](#-c-style-comments-) +//! * [Identifiers](#identifiers) +//! + [`Rust-Style Identifiers`](#rust-style-identifiers) +//! * [Literal Values](#literal-values) +//! + [Escaped Strings](#escaped-strings) +//! + [Integers](#integers) +//! - [Hexadecimal](#hexadecimal) +//! - [Octal](#octal) +//! - [Binary](#binary) +//! - [Decimal](#decimal) +//! + [Floating Point Numbers](#floating-point-numbers) +//! +//! ## Whitespace +//! +//! +//! +//! ### Wrapper combinators that eat whitespace before and after a parser +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! error::ParserError, +//! combinator::delimited, +//! ascii::multispace0, +//! }; +//! +//! /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and +//! /// trailing whitespace, returning the output of `inner`. +//! fn ws<'a, F, O, E: ParserError<&'a str>>(inner: F) -> impl Parser<&'a str, O, E> +//! where +//! F: Parser<&'a str, O, E>, +//! { +//! delimited( +//! multispace0, +//! inner, +//! multispace0 +//! ) +//! } +//! ``` +//! +//! To eat only trailing whitespace, replace `delimited(...)` with `terminated(&inner, multispace0)`. +//! Likewise, the eat only leading whitespace, replace `delimited(...)` with `preceded(multispace0, +//! &inner)`. You can use your own parser instead of `multispace0` if you want to skip a different set +//! of lexemes. +//! +//! ## Comments +//! +//! ### `// C++/EOL-style comments` +//! +//! This version uses `%` to start a comment, does not consume the newline character, and returns an +//! output of `()`. +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! error::ParserError, +//! token::take_till1, +//! }; +//! +//! pub fn peol_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> +//! { +//! ('%', take_till1(['\n', '\r'])) +//! .void() // Output is thrown away. +//! .parse_next(i) +//! } +//! ``` +//! +//! ### `/* C-style comments */` +//! +//! Inline comments surrounded with sentinel tags `(*` and `*)`. This version returns an output of `()` +//! and does not handle nested comments. +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! error::ParserError, +//! token::{tag, take_until0}, +//! }; +//! +//! pub fn pinline_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> { +//! ( +//! "(*", +//! take_until0("*)"), +//! "*)" +//! ) +//! .void() // Output is thrown away. +//! .parse_next(i) +//! } +//! ``` +//! +//! ## Identifiers +//! +//! ### `Rust-Style Identifiers` +//! +//! Parsing identifiers that may start with a letter (or underscore) and may contain underscores, +//! letters and numbers may be parsed like this: +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! stream::AsChar, +//! token::take_while, +//! token::one_of, +//! }; +//! +//! pub fn identifier<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! ( +//! one_of(|c: char| c.is_alpha() || c == '_'), +//! take_while(0.., |c: char| c.is_alphanum() || c == '_') +//! ) +//! .recognize() +//! .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 +//! `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 +//! input text that was parsed, which in this case is the entire `&str` `hello_world123abc`. +//! +//! ## Literal Values +//! +//! ### Escaped Strings +//! +//! ```rust +#![doc = include_str!("../../examples/string/parser.rs")] +//! ``` +//! +//! See also [`escaped`] and [`escaped_transform`]. +//! +//! ### Integers +//! +//! The following recipes all return string slices rather than integer values. How to obtain an +//! integer value instead is demonstrated for hexadecimal integers. The others are similar. +//! +//! The parsers allow the grouping character `_`, which allows one to group the digits by byte, for +//! example: `0xA4_3F_11_28`. If you prefer to exclude the `_` character, the lambda to convert from a +//! string slice to an integer value is slightly simpler. You can also strip the `_` from the string +//! slice that is returned, which is demonstrated in the second hexadecimal number parser. +//! +//! #### Hexadecimal +//! +//! The parser outputs the string slice of the digits without the leading `0x`/`0X`. +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! combinator::alt, +//! 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>> +//! preceded( +//! alt(("0x", "0X")), +//! repeat(1.., +//! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ())) +//! ).map(|()| ()).recognize() +//! ).parse_next(input) +//! } +//! ``` +//! +//! If you want it to return the integer value instead, use map: +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! combinator::alt, +//! combinator::{repeat}, +//! combinator::{preceded, terminated}, +//! token::one_of, +//! token::tag, +//! }; +//! +//! fn hexadecimal_value(input: &mut &str) -> PResult<i64> { +//! preceded( +//! alt(("0x", "0X")), +//! repeat(1.., +//! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ())) +//! ).map(|()| ()).recognize() +//! ).try_map( +//! |out: &str| i64::from_str_radix(&str::replace(&out, "_", ""), 16) +//! ).parse_next(input) +//! } +//! ``` +//! +//! See also [`hex_uint`] +//! +//! #### Octal +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! combinator::alt, +//! combinator::{repeat}, +//! combinator::{preceded, terminated}, +//! token::one_of, +//! token::tag, +//! }; +//! +//! fn octal<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! preceded( +//! alt(("0o", "0O")), +//! repeat(1.., +//! terminated(one_of('0'..='7'), repeat(0.., '_').map(|()| ())) +//! ).map(|()| ()).recognize() +//! ).parse_next(input) +//! } +//! ``` +//! +//! #### Binary +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! combinator::alt, +//! combinator::{repeat}, +//! combinator::{preceded, terminated}, +//! token::one_of, +//! token::tag, +//! }; +//! +//! fn binary<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! preceded( +//! alt(("0b", "0B")), +//! repeat(1.., +//! terminated(one_of('0'..='1'), repeat(0.., '_').map(|()| ())) +//! ).map(|()| ()).recognize() +//! ).parse_next(input) +//! } +//! ``` +//! +//! #### Decimal +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! combinator::{repeat}, +//! combinator::terminated, +//! token::one_of, +//! }; +//! +//! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! repeat(1.., +//! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ())) +//! ).map(|()| ()) +//! .recognize() +//! .parse_next(input) +//! } +//! ``` +//! +//! See also [`dec_uint`] and [`dec_int`] +//! +//! ### Floating Point Numbers +//! +//! The following is adapted from [the Python parser by Valentin Lorentz](https://github.com/ProgVal/rust-python-parser/blob/master/src/numbers.rs). +//! +//! ```rust +//! use winnow::prelude::*; +//! use winnow::{ +//! combinator::alt, +//! combinator::{repeat}, +//! combinator::opt, +//! combinator::{preceded, terminated}, +//! token::one_of, +//! }; +//! +//! fn float<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! alt(( +//! // Case one: .42 +//! ( +//! '.', +//! decimal, +//! opt(( +//! one_of(['e', 'E']), +//! opt(one_of(['+', '-'])), +//! decimal +//! )) +//! ).recognize() +//! , // Case two: 42e42 and 42.42e42 +//! ( +//! decimal, +//! opt(preceded( +//! '.', +//! decimal, +//! )), +//! one_of(['e', 'E']), +//! opt(one_of(['+', '-'])), +//! decimal +//! ).recognize() +//! , // Case three: 42. and 42.42 +//! ( +//! decimal, +//! '.', +//! opt(decimal) +//! ).recognize() +//! )).parse_next(input) +//! } +//! +//! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! repeat(1.., +//! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ())) +//! ). +//! map(|()| ()) +//! .recognize() +//! .parse_next(input) +//! } +//! ``` +//! +//! See also [`float`] + +#![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; diff --git a/vendor/winnow/src/_topic/mod.rs b/vendor/winnow/src/_topic/mod.rs new file mode 100644 index 0000000..79ba28a --- /dev/null +++ b/vendor/winnow/src/_topic/mod.rs @@ -0,0 +1,37 @@ +//! # Special Topics +//! +//! These are short recipes for accomplishing common tasks. +//! +//! - [Why `winnow`?][why] +//! - Formats: +//! - [Elements of Programming Languages][language] +//! - [Arithmetic][arithmetic] +//! - [s-expression][s_expression] +//! - [json] +//! - [INI][ini] +//! - [HTTP][http] +//! - Special Topics: +//! - [Implementing `FromStr`][fromstr] +//! - [Performance][performance] +//! - [Parsing Partial Input][partial] +//! - [Custom stream or token][stream] +//! - [Custom errors][error] +//! +//! See also parsers written with `winnow`: +//! +//! - [`toml_edit`](https://crates.io/crates/toml_edit) +//! - [`hcl-edit`](https://crates.io/crates/hcl-edit) +#![allow(clippy::std_instead_of_core)] + +pub mod arithmetic; +pub mod error; +pub mod fromstr; +pub mod http; +pub mod ini; +pub mod json; +pub mod language; +pub mod partial; +pub mod performance; +pub mod s_expression; +pub mod stream; +pub mod why; diff --git a/vendor/winnow/src/_topic/partial.rs b/vendor/winnow/src/_topic/partial.rs new file mode 100644 index 0000000..19895d3 --- /dev/null +++ b/vendor/winnow/src/_topic/partial.rs @@ -0,0 +1,46 @@ +//! # Parsing Partial Input +//! +//! Typically, the input being parsed is all in-memory, or is complete. Some data sources are too +//! large to fit into memory, only allowing parsing an incomplete or [`Partial`] subset of the +//! data, requiring incrementally parsing. +//! +//! By wrapping a stream, like `&[u8]`, with [`Partial`], parsers will report when the data is +//! [`Incomplete`] and more input is [`Needed`], allowing the caller to stream-in additional data +//! to be parsed. The data is then parsed a chunk at a time. +//! +//! Chunks are typically defined by either: +//! - A header reporting the number of bytes, like with [`length_value`] +//! - [`Partial`] can explicitly be changed to being complete once the specified bytes are +//! acquired via [`StreamIsPartial::complete`]. +//! - A delimiter, like with [ndjson](http://ndjson.org/) +//! - You can parse up-to the delimiter or do a `take_until0(delim).and_then(parser)` +//! +//! If the chunks are not homogeneous, a state machine will be needed to track what the expected +//! parser is for the next chunk. +//! +//! Caveats: +//! - `winnow` takes the approach of re-parsing from scratch. Chunks should be relatively small to +//! prevent the re-parsing overhead from dominating. +//! - Parsers like [`repeat`] do not know when an `eof` is from insufficient data or the end of the +//! stream, causing them to always report [`Incomplete`]. +//! +//! # Example +//! +//! `main.rs`: +//! ```rust,ignore +#![doc = include_str!("../../examples/ndjson/main.rs")] +//! ``` +//! +//! `parser.rs`: +//! ```rust,ignore +#![doc = include_str!("../../examples/ndjson/parser.rs")] +//! ``` + +#![allow(unused_imports)] // Used for intra-doc links + +use crate::binary::length_value; +use crate::combinator::repeat; +use crate::error::ErrMode::Incomplete; +use crate::error::Needed; +use crate::stream::Partial; +use crate::stream::StreamIsPartial; diff --git a/vendor/winnow/src/_topic/performance.rs b/vendor/winnow/src/_topic/performance.rs new file mode 100644 index 0000000..8a6555a --- /dev/null +++ b/vendor/winnow/src/_topic/performance.rs @@ -0,0 +1,55 @@ +//! # Performance +//! +//! ## Runtime Performance +//! +//! See also the general Rust [Performance Book](https://nnethercote.github.io/perf-book/) +//! +//! Tips +//! - Try `cargo add winnow -F simd`. For some it offers significant performance improvements +//! - When enough cases of an [`alt`] have unique prefixes, prefer [`dispatch`] +//! - When parsing text, try to parse as bytes (`u8`) rather than `char`s ([`BStr`] can make +//! debugging easier) +//! - Find simplified subsets of the grammar to parse, falling back to the full grammar when it +//! doesn't work. For example, when parsing json strings, parse them without support for escapes, +//! falling back to escape support if it fails. +//! - Watch for large return types. A surprising place these can show up is when chaining parsers +//! with a tuple. +//! +//! ## Build-time Performance +//! +//! Returning complex types as `impl Trait` can negatively impact build times. This can hit in +//! surprising cases like: +//! ```rust +//! # use winnow::prelude::*; +//! fn foo<I, O, E>() -> impl Parser<I, O, E> +//! # where +//! # I: winnow::stream::Stream<Token=O>, +//! # I: winnow::stream::StreamIsPartial, +//! # E: winnow::error::ParserError<I>, +//! { +//! // ...some chained combinators... +//! # winnow::token::any +//! } +//! ``` +//! +//! Instead, wrap the combinators in a closure to simplify the type: +//! ```rust +//! # use winnow::prelude::*; +//! fn foo<I, O, E>() -> impl Parser<I, O, E> +//! # where +//! # I: winnow::stream::Stream<Token=O>, +//! # I: winnow::stream::StreamIsPartial, +//! # E: winnow::error::ParserError<I>, +//! { +//! move |input: &mut I| { +//! // ...some chained combinators... +//! # winnow::token::any +//! .parse_next(input) +//! } +//! } +//! ``` + +#![allow(unused_imports)] +use crate::combinator::alt; +use crate::combinator::dispatch; +use crate::stream::BStr; diff --git a/vendor/winnow/src/_topic/s_expression.rs b/vendor/winnow/src/_topic/s_expression.rs new file mode 100644 index 0000000..ef66600 --- /dev/null +++ b/vendor/winnow/src/_topic/s_expression.rs @@ -0,0 +1,5 @@ +//! # s-expression +//! +//! ```rust +#![doc = include_str!("../../examples/s_expression/parser.rs")] +//! ``` diff --git a/vendor/winnow/src/_topic/stream.rs b/vendor/winnow/src/_topic/stream.rs new file mode 100644 index 0000000..2a9a79b --- /dev/null +++ b/vendor/winnow/src/_topic/stream.rs @@ -0,0 +1,66 @@ +//! # Custom [`Stream`] +//! +//! `winnow` is batteries included with support for +//! - Basic inputs like `&str`, newtypes with +//! - 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 +//! +//! 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`. +//! +//! ## Implementing a custom stream +//! +//! Let's assume we have an input type we'll call `MyStream`. +//! `MyStream` is a sequence of `MyItem` type. +//! +//! 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>> { +//! "test".parse_next(i) +//! } +//! ``` +//! +//! Here are the traits you may have to implement for `MyStream`: +//! +//! | trait | usage | +//! |---|---| +//! | [`Stream`] |Core trait for driving parsing| +//! | [`StreamIsPartial`] | Marks the input as being the complete buffer or a partial buffer for streaming input | +//! | [`AsBytes`] |Casts the input type to a byte slice| +//! | [`AsBStr`] |Casts the input type to a slice of ASCII / UTF-8-like bytes| +//! | [`Compare`] |Character comparison operations| +//! | [`FindSlice`] |Look for a substring in self| +//! | [`Location`] |Calculate location within initial input| +//! | [`Offset`] |Calculate the offset between slices| +//! +//! And for `MyItem`: +//! +//! | trait | usage | +//! |---|---| +//! | [`AsChar`] |Transforms common types to a char for basic token parsing| +//! | [`ContainsToken`] |Look for the token in the given set| +//! +//! And traits for `&[MyItem]`: +//! +//! | trait | usage | +//! |---|---| +//! | [`SliceLen`] |Calculate the input length| +//! | [`ParseSlice`] |Used to integrate `&str`'s `parse()` method| +//! +//! ## Implementing a custom token +//! +//! If you are parsing `&[Myitem]`, leaving just the `MyItem` traits. +//! +//! For example: +//! ```rust +#![doc = include_str!("../../examples/arithmetic/parser_lexer.rs")] +//! ``` + +#[allow(unused_imports)] // Here for intra-dock links +use crate::stream::*; diff --git a/vendor/winnow/src/_topic/why.rs b/vendor/winnow/src/_topic/why.rs new file mode 100644 index 0000000..e0328f1 --- /dev/null +++ b/vendor/winnow/src/_topic/why.rs @@ -0,0 +1,98 @@ +//! # Why `winnow`? +//! +//! To answer this question, it will be useful to contrast this with other approaches to parsing. +//! +//! **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). +//! +//! ## Hand-written parsers +//! +//! Typically, a hand-written parser gives you the flexibility to get +//! - Fast parse performance +//! - Fast compile-time +//! - Small binary sizes +//! - High quality error message +//! - Fewer dependencies to audit +//! +//! However, this comes at the cost of doing it all yourself, including +//! - Optimizing for each of the above characteristics you care about +//! - Ensuring the safety of any `unsafe` code (buffer overflows being a common bug with parsers) +//! - Being aware of, familiar with, and correctly implement the relevant algorithms. +//! matklad, who has written two rust compile frontends, commented +//! ["I’ve implemented a production-grade Pratt parser once, but I no longer immediately understand that code :-)"](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) +//! +//! This approach works well if: +//! - Your format is small and is unlikely to change +//! - Your format is large but you have people who can focus solely on parsing, like with large +//! programming languages +//! +//! ## `winnow` +//! +//! Unlike traditional programming language parsers that use +//! [lex](https://en.wikipedia.org/wiki/Lex_(software)) or +//! [yacc](https://en.wikipedia.org/wiki/Yacc), you can think of `winnow` as a general version of +//! the helpers you would create along the way to writing a hand-written parser. +//! +//! `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 +//! +//! For binary formats, `winnow` includes: +//! - [A hexadecimal view][crate::Bytes] in [traces][crate::trace] +//! - [TLV](https://en.wikipedia.org/wiki/Type-length-value) +//! - 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] +//! - Ability to evaluate directly, parse to an AST, or lex and parse the format +//! +//! This works well for: +//! - Prototyping for what will be a hand-written parser +//! - When you want to minimize the work to evolve your format +//! - When you don't have contributors focused solely on parsing and your grammar is large enough +//! to be unwieldy to hand write. +//! +//! ## `nom` +//! +//! `winnow` is a fork of the venerable [`nom`](https://crates.io/crates/nom). The difference +//! between them is largely in priorities. `nom` prioritizes: +//! - Lower churn for existing users while `winnow` is trying to find ways to make things better +//! for the parsers yet to be written. +//! - Having a small core, relying on external crates like +//! [`nom-locate`](https://crates.io/crates/nom_locate) and +//! [`nom-supreme`](https://crates.io/crates/nom-supreme), encouraging flexibility among users +//! and to not block users on new features being merged while `winnow` aims to include all the +//! fundamentals for parsing to ensure the experience is cohesive and high quality. +//! +//! ## `chumsky` +//! +//! [`chumsky`](https://crates.io/crates/chumsky) is an up and coming parser-combinator library +//! that includes advanced features like error recovery. +//! +//! Probably the biggest diverging philosophy is `chumsky`s stance: +//! +//! > "If you need to implement either `Parser` or `Strategy` by hand, that's a problem that needs fixing". +//! +//! This is under "batteries included" but it also ties into the feeling that `chumksy` acts more like +//! a framework. Instead of composing together helpers, you are expected to do everything through +//! their system to the point that it is non-trivial to implement their `Parser` trait and are +//! encouraged to use the +//! [`custom`](https://docs.rs/chumsky/0.9.0/chumsky/primitive/fn.custom.html) helper. This +//! requires re-framing everything to fit within their model and makes the code harder to understand +//! and debug as you are working with abstract operations that will eventually be applied +//! rather than directly with the parsers. +//! +//! In contrast, `winnow` is an introspectable toolbox that can easily be customized at any level. +//! Probably the biggest thing that `winnow` loses out on is optimizations from ["parse modes" via +//! GATs](https://github.com/zesterer/chumsky/pull/82) which allows downstream parsers to tell +//! upstream parsers when information will be discarded, allowing bypassing expensive operations, +//! like allocations. This requires a lot more complex interaction with parsers that isn't as +//! trivial to do with bare functions which would lose out on any of that side-band information. +//! Instead, we work around this with things like the [`Accumulate`] trait. + +#![allow(unused_imports)] +use crate::stream::Accumulate; diff --git a/vendor/winnow/src/_tutorial/chapter_0.rs b/vendor/winnow/src/_tutorial/chapter_0.rs new file mode 100644 index 0000000..35a2d14 --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_0.rs @@ -0,0 +1,39 @@ +//! # Chapter 0: Introduction +//! +//! This tutorial assumes that you are: +//! - Already familiar with Rust +//! - Using `winnow` for the first time +//! +//! The focus will be on parsing in-memory strings (`&str`). Once done, you might want to check the +//! [Special Topics][_topic] for more specialized topics or examples. +//! +//! ## About +//! +//! `winnow` is a parser-combinator library. In other words, it gives you tools to define: +//! - "parsers", or functions that take an input and give back an output +//! - "combinators", or functions that take parsers and _combine_ them together! +//! +//! While "combinator" might be an unfamiliar word, you are likely using them in your rust code +//! today, like with the [`Iterator`] trait: +//! ```rust +//! let data = vec![1, 2, 3, 4, 5]; +//! let even_count = data.iter() +//! .copied() // combinator +//! .filter(|d| d % 2 == 0) // combinator +//! .count(); // combinator +//! ``` +//! +//! 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 + +#![allow(unused_imports)] +use crate::_topic; +use std::iter::Iterator; + +pub use super::chapter_1 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_1.rs b/vendor/winnow/src/_tutorial/chapter_1.rs new file mode 100644 index 0000000..9824aa9 --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_1.rs @@ -0,0 +1,86 @@ +//! # 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"). +//! +//! Let's discuss what a "parser" actually does. A parser takes an input and 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. +//! +//! ```text +//! ┌─► Ok(what matched the parser) +//! ┌─────────┐ │ +//! my input───►│my parser├──►either──┤ +//! └─────────┘ └─► Err(...) +//! ``` +//! +//! +//! To represent this model of the world, winnow uses the [`PResult<O>`] type. +//! The `Ok` variant has `output: O`; +//! whereas the `Err` variant stores an error. +//! +//! You can import that from: +//! +//! ```rust +//! use winnow::PResult; +//! ``` +//! +//! 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. +//! +//! 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. +//! +//! To make it easier to implement a [`Parser`], the trait is implemented for +//! functions of the form `Fn(&mut I) -> PResult<O>`. +//! +//! 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 doesn't parse anything, it also should just return an empty string. +//! +//! ```rust +//! use winnow::PResult; +//! use winnow::Parser; +//! +//! pub fn do_nothing_parser<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! Ok("") +//! } +//! +//! fn main() { +//! let mut input = "0x1a2b Hello"; +//! +//! let output = do_nothing_parser.parse_next(&mut input).unwrap(); +//! // Same as: +//! // let output = do_nothing_parser(&mut input).unwrap(); +//! +//! assert_eq!(input, "0x1a2b Hello"); +//! assert_eq!(output, ""); +//! } +//! ``` + +#![allow(unused_imports)] +use crate::PResult; +use crate::Parser; + +pub use super::chapter_0 as previous; +pub use super::chapter_2 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_2.rs b/vendor/winnow/src/_tutorial/chapter_2.rs new file mode 100644 index 0000000..4ae07ac --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_2.rs @@ -0,0 +1,248 @@ +//! # Chapter 2: Tokens and Tags +//! +//! The simplest *useful* parser you can write is one which matches tokens. +//! +//! ## Tokens +//! +//! [`Stream`] provides some core operations to help with parsing. For example, to process a +//! single token, you can do: +//! ```rust +//! # use winnow::Parser; +//! # use winnow::PResult; +//! use winnow::stream::Stream; +//! use winnow::error::ParserError; +//! use winnow::error::ErrorKind; +//! use winnow::error::ErrMode; +//! +//! fn parse_prefix(input: &mut &str) -> PResult<char> { +//! let c = input.next_token().ok_or_else(|| { +//! ErrMode::from_error_kind(input, ErrorKind::Token) +//! })?; +//! 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()); +//! } +//! ``` +//! +//! [`any`] and [`Parser::verify`] are [`Parser`] building blocks on top of [`Stream`]: +//! ```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) +//! } +//! # +//! # 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()); +//! # } +//! ``` +//! +//! Matching a single token literal is common enough that [`Parser`] is implemented for +//! `char`. +//! +//! ```rust +//! # use winnow::PResult; +//! use winnow::Parser; +//! +//! fn parse_prefix(input: &mut &str) -> PResult<char> { +//! '0'.parse_next(input) +//! } +//! # +//! # 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()); +//! # } +//! ``` +//! +//! ## Tags +//! +//! [`Stream`] also supports processing slices of tokens: +//! ```rust +//! # use winnow::Parser; +//! # use winnow::PResult; +//! use winnow::stream::Stream; +//! use winnow::error::ParserError; +//! use winnow::error::ErrorKind; +//! use winnow::error::ErrMode; +//! +//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! let expected = "0x"; +//! if input.len() < expected.len() { +//! return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); +//! } +//! let actual = input.next_slice(expected.len()); +//! if actual != expected { +//! return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); +//! } +//! 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()); +//! } +//! ``` +//! +//! Again, matching a literal is common enough that [`Parser`] is implemented for `&str`: +//! ```rust +//! # use winnow::PResult; +//! use winnow::Parser; +//! +//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! "0x".parse_next(input) +//! } +//! # +//! # 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()); +//! # } +//! ``` +//! +//! In `winnow`, we call this type of parser a [`tag`]. 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 +//! `chars` of a specific class, like digits. For this, we use the [`one_of`] parer: +//! +//! ```rust +//! # use winnow::Parser; +//! # use winnow::PResult; +//! use winnow::token::one_of; +//! +//! fn parse_digits(input: &mut &str) -> PResult<char> { +//! one_of(('0'..='9', 'a'..='f', 'A'..='F')).parse_next(input) +//! } +//! +//! fn main() { +//! let mut input = "1a2b Hello"; +//! +//! let output = parse_digits.parse_next(&mut input).unwrap(); +//! assert_eq!(input, "a2b Hello"); +//! assert_eq!(output, '1'); +//! +//! assert!(parse_digits.parse_next(&mut "Z").is_err()); +//! } +//! ``` +//! +//! > **Aside:** [`one_of`] might look straightforward, a function returning a value that implements `Parser`. +//! > Let's look at it more closely as its used above (resolving all generic parameters): +//! > ```rust +//! > # use winnow::prelude::*; +//! > # use winnow::error::InputError; +//! > pub fn one_of<'i>( +//! > list: &'static [char] +//! > ) -> impl Parser<&'i str, char, InputError<&'i str>> { +//! > // ... +//! > # winnow::token::one_of(list) +//! > } +//! > ``` +//! > If you have not programmed in a language where functions are values, the type signature of the +//! > [`one_of`] function might be a surprise. +//! > The function [`one_of`] *returns a function*. The function it returns is a +//! > `Parser`, taking a `&str` and returning an `PResult`. This is a common pattern in winnow for +//! > configurable or stateful parsers. +//! +//! Some of character classes are common enough that a named parser is provided, like with: +//! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`) +//! - [`newline`][crate::ascii::newline]: Matches a newline character `\n` +//! - [`tab`][crate::ascii::tab]: Matches a tab character `\t` +//! +//! You can then capture sequences of these characters with parsers like [`take_while`]. +//! ```rust +//! # use winnow::Parser; +//! # use winnow::PResult; +//! use winnow::token::take_while; +//! +//! fn parse_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 = "1a2b Hello"; +//! +//! let output = parse_digits.parse_next(&mut input).unwrap(); +//! assert_eq!(input, " Hello"); +//! assert_eq!(output, "1a2b"); +//! +//! assert!(parse_digits.parse_next(&mut "Z").is_err()); +//! } +//! ``` +//! +//! We could simplify this further by using one of the built-in character classes, [`hex_digit1`]: +//! ```rust +//! # use winnow::Parser; +//! # use winnow::PResult; +//! use winnow::ascii::hex_digit1; +//! +//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! hex_digit1.parse_next(input) +//! } +//! +//! fn main() { +//! let mut input = "1a2b Hello"; +//! +//! let output = parse_digits.parse_next(&mut input).unwrap(); +//! assert_eq!(input, " Hello"); +//! assert_eq!(output, "1a2b"); +//! +//! assert!(parse_digits.parse_next(&mut "Z").is_err()); +//! } +//! ``` +//! +//! See [`ascii`] for more text-based parsers. + +#![allow(unused_imports)] +use crate::ascii; +use crate::ascii::hex_digit1; +use crate::stream::ContainsToken; +use crate::stream::Stream; +use crate::token; +use crate::token::any; +use crate::token::one_of; +use crate::token::tag; +use crate::token::take_while; +use crate::Parser; +use std::ops::RangeInclusive; + +pub use super::chapter_1 as previous; +pub use super::chapter_3 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_3.rs b/vendor/winnow/src/_tutorial/chapter_3.rs new file mode 100644 index 0000000..a2cbba2 --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_3.rs @@ -0,0 +1,376 @@ +//! # Chapter 3: Sequencing and Alternatives +//! +//! In the last chapter, we saw how to create simple parsers using prebuilt parsers. +//! +//! In this chapter, we explore two other widely used features: +//! alternatives and composition. +//! +//! ## Sequencing +//! +//! Now that we can create more interesting parsers, we can sequence them together, like: +//! +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # +//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! "0x".parse_next(input) +//! } +//! +//! fn parse_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 = parse_prefix.parse_next(&mut input).unwrap(); +//! let digits = parse_digits.parse_next(&mut input).unwrap(); +//! +//! assert_eq!(prefix, "0x"); +//! assert_eq!(digits, "1a2b"); +//! assert_eq!(input, " Hello"); +//! } +//! ``` +//! +//! To sequence these together, you can just put them in a tuple: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # +//! # fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! # "0x".parse_next(input) +//! # } +//! # +//! # fn parse_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_prefix, +//! parse_digits +//! ).parse_next(&mut input).unwrap(); +//! +//! assert_eq!(prefix, "0x"); +//! assert_eq!(digits, "1a2b"); +//! assert_eq!(input, " Hello"); +//! } +//! ``` +//! +//! Frequently, you won't care about the tag and you can instead use one of the provided combinators, +//! like [`preceded`]: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! use winnow::combinator::preceded; +//! +//! # fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! # "0x".parse_next(input) +//! # } +//! # +//! # fn parse_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 digits = preceded( +//! parse_prefix, +//! parse_digits +//! ).parse_next(&mut input).unwrap(); +//! +//! assert_eq!(digits, "1a2b"); +//! assert_eq!(input, " Hello"); +//! } +//! ``` +//! +//! See [`combinator`] for more sequencing parsers. +//! +//! ## Alternatives +//! +//! Sometimes, we might want to choose between two parsers; and we're happy with +//! either being used. +//! +//! [`Stream::checkpoint`] helps us to retry parsing: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! use winnow::stream::Stream; +//! +//! 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); +//! if let Ok(output) = ("0o", parse_oct_digits).parse_next(input) { +//! return Ok(output); +//! } +//! +//! input.reset(start); +//! if let Ok(output) = ("0d", parse_dec_digits).parse_next(input) { +//! return Ok(output); +//! } +//! +//! 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'), +//! # )).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"); +//! +//! assert!(parse_digits(&mut "ghiWorld").is_err()); +//! } +//! ``` +//! +//! > **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] +//! +//! [`opt`] is a basic building block for correctly handling retrying parsing: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! use winnow::combinator::opt; +//! +//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { +//! if let Some(output) = opt(("0b", parse_bin_digits)).parse_next(input)? { +//! Ok(output) +//! } else if let Some(output) = opt(("0o", parse_oct_digits)).parse_next(input)? { +//! Ok(output) +//! } else if let Some(output) = opt(("0d", parse_dec_digits)).parse_next(input)? { +//! Ok(output) +//! } else { +//! ("0x", parse_hex_digits).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"); +//! # +//! # assert!(parse_digits(&mut "ghiWorld").is_err()); +//! # } +//! ``` +//! +//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the `else`: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! use winnow::combinator::alt; +//! +//! 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'), +//! # )).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"); +//! # +//! # assert!(parse_digits(&mut "ghiWorld").is_err()); +//! # } +//! ``` +//! +//! > **Note:** [`success`] and [`fail`] are parsers that might be useful in the `else` case. +//! +//! 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 +//! [`dispatch`] macro: +//! +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! use winnow::combinator::dispatch; +//! use winnow::token::take; +//! use winnow::combinator::fail; +//! +//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! dispatch!(take(2usize); +//! "0b" => parse_bin_digits, +//! "0o" => parse_oct_digits, +//! "0d" => parse_dec_digits, +//! "0x" => parse_hex_digits, +//! _ => fail, +//! ).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 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. +//! +//! See [`combinator`] for more alternative parsers. + +#![allow(unused_imports)] +use super::chapter_6; +use crate::combinator; +use crate::combinator::alt; +use crate::combinator::dispatch; +use crate::combinator::fail; +use crate::combinator::opt; +use crate::combinator::peek; +use crate::combinator::preceded; +use crate::combinator::success; +use crate::stream::Stream; + +pub use super::chapter_2 as previous; +pub use super::chapter_4 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_4.rs b/vendor/winnow/src/_tutorial/chapter_4.rs new file mode 100644 index 0000000..59aa7ee --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_4.rs @@ -0,0 +1,110 @@ +//! # Chapter 4: Parsers With Custom Return Types +//! +//! So far, we have seen mostly functions that take an `&str`, and return a +//! `PResult<&str>`. Splitting strings into smaller strings and characters is certainly +//! useful, but it's not the only thing winnow is capable of! +//! +//! A useful operation when parsing is to convert between types; for example +//! parsing from `&str` to another primitive, like [`usize`]. +//! +//! All we need to do for our parser to return a different type is to change +//! the type parameter of [`PResult`] to the desired return type. +//! For example, to return a `usize`, return a `PResult<usize>`. +//! Recall that the type parameter of the `PResult` is the input +//! type, so even if you're returning something different, if your input +//! is a `&str`, the type argument of `PResult` should be also. +//! +//! One winnow-native way of doing a type conversion is to use the +//! [`Parser::parse_to`] combinator +//! to convert from a successful parse to a particular type using [`FromStr`]. +//! +//! The following code converts from a string containing a number to `usize`: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::ascii::digit1; +//! # +//! fn parse_digits(input: &mut &str) -> PResult<usize> { +//! digit1 +//! .parse_to() +//! .parse_next(input) +//! } +//! +//! fn main() { +//! let mut input = "1024 Hello"; +//! +//! let output = parse_digits.parse_next(&mut input).unwrap(); +//! assert_eq!(input, " Hello"); +//! assert_eq!(output, 1024); +//! +//! assert!(parse_digits(&mut "Z").is_err()); +//! } +//! ``` +//! +//! `Parser::parse_to` is just a convenient form of [`Parser::try_map`] which we can use to handle +//! all radices of numbers: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! use winnow::combinator::dispatch; +//! use winnow::token::take; +//! use winnow::combinator::fail; +//! +//! fn parse_digits(input: &mut &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'), +//! # )).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 digits = parse_digits.parse_next(&mut input).unwrap(); +//! +//! assert_eq!(input, " Hello"); +//! assert_eq!(digits, 0x1a2b); +//! +//! assert!(parse_digits(&mut "ghiWorld").is_err()); +//! } +//! ``` +//! +//! See also [`Parser`] for more output-modifying parsers. + +#![allow(unused_imports)] +use crate::PResult; +use crate::Parser; +use std::str::FromStr; + +pub use super::chapter_3 as previous; +pub use super::chapter_5 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_5.rs b/vendor/winnow/src/_tutorial/chapter_5.rs new file mode 100644 index 0000000..d507bde --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_5.rs @@ -0,0 +1,282 @@ +//! # Chapter 5: Repetition +//! +//! In [`chapter_3`], we covered how to sequence different parsers into a tuple but sometimes you need to run a +//! single parser multiple times, collecting the result into a container, like [`Vec`]. +//! +//! Let's collect the result of `parse_digits`: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # use winnow::combinator::dispatch; +//! # use winnow::token::take; +//! # use winnow::combinator::fail; +//! use winnow::combinator::opt; +//! use winnow::combinator::repeat; +//! use winnow::combinator::terminated; +//! +//! fn parse_list(input: &mut &str) -> PResult<Vec<usize>> { +//! let mut list = Vec::new(); +//! while let Some(output) = opt(terminated(parse_digits, opt(','))).parse_next(input)? { +//! list.push(output); +//! } +//! Ok(list) +//! } +//! +//! // ... +//! # fn parse_digits(input: &mut &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'), +//! # )).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,0x3c4d,0x5e6f Hello"; +//! +//! let digits = parse_list.parse_next(&mut input).unwrap(); +//! +//! assert_eq!(input, " Hello"); +//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]); +//! +//! assert!(parse_digits(&mut "ghiWorld").is_err()); +//! } +//! ``` +//! +//! We can implement this declaratively with [`repeat`]: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # use winnow::combinator::dispatch; +//! # use winnow::token::take; +//! # use winnow::combinator::fail; +//! use winnow::combinator::opt; +//! use winnow::combinator::repeat; +//! use winnow::combinator::terminated; +//! +//! fn parse_list(input: &mut &str) -> PResult<Vec<usize>> { +//! repeat(0.., +//! terminated(parse_digits, opt(',')) +//! ).parse_next(input) +//! } +//! # +//! # fn parse_digits(input: &mut &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'), +//! # )).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,0x3c4d,0x5e6f Hello"; +//! # +//! # let digits = parse_list.parse_next(&mut input).unwrap(); +//! # +//! # assert_eq!(input, " Hello"); +//! # assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]); +//! # +//! # assert!(parse_digits(&mut "ghiWorld").is_err()); +//! # } +//! ``` +//! +//! You'll notice that the above allows trailing `,` when we intended to not support that. We can +//! easily fix this by using [`separated0`]: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # use winnow::combinator::dispatch; +//! # use winnow::token::take; +//! # use winnow::combinator::fail; +//! use winnow::combinator::separated0; +//! +//! fn parse_list(input: &mut &str) -> PResult<Vec<usize>> { +//! separated0(parse_digits, ",").parse_next(input) +//! } +//! +//! // ... +//! # fn parse_digits(input: &mut &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'), +//! # )).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,0x3c4d,0x5e6f Hello"; +//! +//! let digits = parse_list.parse_next(&mut input).unwrap(); +//! +//! assert_eq!(input, " Hello"); +//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]); +//! +//! assert!(parse_digits(&mut "ghiWorld").is_err()); +//! } +//! ``` +//! +//! 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: +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # use winnow::combinator::dispatch; +//! # use winnow::token::take; +//! # use winnow::combinator::fail; +//! # use winnow::combinator::separated0; +//! # +//! fn recognize_list<'s>(input: &mut &'s str) -> PResult<&'s str> { +//! parse_list.recognize().parse_next(input) +//! } +//! +//! fn parse_list(input: &mut &str) -> PResult<()> { +//! separated0(parse_digits, ",").parse_next(input) +//! } +//! +//! # fn parse_digits(input: &mut &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'), +//! # )).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,0x3c4d,0x5e6f Hello"; +//! +//! let digits = recognize_list.parse_next(&mut input).unwrap(); +//! +//! assert_eq!(input, " Hello"); +//! assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f"); +//! +//! assert!(parse_digits(&mut "ghiWorld").is_err()); +//! } +//! ``` +//! See [`combinator`] for more repetition parsers. + +#![allow(unused_imports)] +use super::chapter_2; +use super::chapter_3; +use crate::combinator; +use crate::combinator::repeat; +use crate::combinator::separated0; +use crate::stream::Accumulate; +use crate::Parser; +use std::vec::Vec; + +pub use super::chapter_4 as previous; +pub use super::chapter_6 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_6.rs b/vendor/winnow/src/_tutorial/chapter_6.rs new file mode 100644 index 0000000..9f42309 --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_6.rs @@ -0,0 +1,156 @@ +//! # Chapter 6: Error Reporting +//! +//! ## `Error` +//! +//! 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. +//! +//! ```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::ErrMode; +//! pub type OResult<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::error::StrContext; +//! use winnow::combinator::cut_err; +//! +//! 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) +//! } +//! +//! // ... +//! # 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"); +//! } +//! ``` +//! 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 crate::error::ErrMode; +use crate::error::ErrMode::*; +use crate::error::ErrorKind; +use crate::PResult; +use crate::Parser; +use crate::_topic; + +pub use super::chapter_5 as previous; +pub use super::chapter_7 as next; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/chapter_7.rs b/vendor/winnow/src/_tutorial/chapter_7.rs new file mode 100644 index 0000000..7f13f98 --- /dev/null +++ b/vendor/winnow/src/_tutorial/chapter_7.rs @@ -0,0 +1,118 @@ +//! # Chapter 7: Integrating the Parser +//! +//! So far, we've highlighted how to incrementally parse, but how do we bring this all together +//! into our application? +//! +//! 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`]) +//! ```rust +//! # use winnow::prelude::*; +//! # use winnow::token::take_while; +//! # use winnow::combinator::dispatch; +//! # 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()) +//! } +//! } +//! +//! // ... +//! # 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'), +//! # )).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 = "0x1a2b"; +//! assert_eq!(input.parse::<Hex>().unwrap(), Hex(0x1a2b)); +//! +//! let input = "0x1a2b Hello"; +//! assert!(input.parse::<Hex>().is_err()); +//! let input = "ghiHello"; +//! assert!(input.parse::<Hex>().is_err()); +//! } +//! ``` + +#![allow(unused_imports)] +use super::chapter_1; +use crate::combinator::eof; +use crate::error::ErrMode; +use crate::error::InputError; +use crate::error::ParseError; +use crate::PResult; +use crate::Parser; + +pub use super::chapter_6 as previous; +pub use crate::_tutorial as table_of_contents; diff --git a/vendor/winnow/src/_tutorial/mod.rs b/vendor/winnow/src/_tutorial/mod.rs new file mode 100644 index 0000000..2a4bd61 --- /dev/null +++ b/vendor/winnow/src/_tutorial/mod.rs @@ -0,0 +1,13 @@ +//! # Tutorial +//! +//! Table of Contents +#![allow(clippy::std_instead_of_core)] + +pub mod chapter_0; +pub mod chapter_1; +pub mod chapter_2; +pub mod chapter_3; +pub mod chapter_4; +pub mod chapter_5; +pub mod chapter_6; +pub mod chapter_7; diff --git a/vendor/winnow/src/ascii/mod.rs b/vendor/winnow/src/ascii/mod.rs new file mode 100644 index 0000000..eddf0f0 --- /dev/null +++ b/vendor/winnow/src/ascii/mod.rs @@ -0,0 +1,1675 @@ +//! Character specific parsers and combinators +//! +//! Functions recognizing specific characters + +#[cfg(test)] +mod tests; + +use crate::lib::std::ops::{Add, Shl}; + +use crate::combinator::alt; +use crate::combinator::cut_err; +use crate::combinator::opt; +use crate::error::ParserError; +use crate::error::{ErrMode, ErrorKind, Needed}; +use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial}; +use crate::stream::{Compare, CompareResult}; +use crate::token::one_of; +use crate::token::take_till0; +use crate::token::take_while; +use crate::trace::trace; +use crate::PResult; +use crate::Parser; + +/// Recognizes the string `"\r\n"`. +/// +/// *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. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; +/// # use winnow::ascii::crlf; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// crlf.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n"))); +/// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::crlf; +/// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n"))); +/// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + I: Compare<&'static str>, +{ + trace("crlf", "\r\n").parse_next(input) +} + +/// Recognizes a string of any char except `"\r\n"` or `"\n"`. +/// +/// *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. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::not_line_ending; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// not_line_ending.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("ab\r\nc"), Ok(("\r\nc", "ab"))); +/// assert_eq!(parser.parse_peek("ab\nc"), Ok(("\nc", "ab"))); +/// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc"))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// assert_eq!(parser.parse_peek("a\rb\nc"), Err(ErrMode::Backtrack(InputError::new("\rb\nc", ErrorKind::Tag )))); +/// assert_eq!(parser.parse_peek("a\rbc"), Err(ErrMode::Backtrack(InputError::new("\rbc", ErrorKind::Tag )))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::not_line_ending; +/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Ok((Partial::new("\r\nc"), "ab"))); +/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rb\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rb\nc"), ErrorKind::Tag )))); +/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rbc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rbc"), ErrorKind::Tag )))); +/// ``` +#[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, +{ + trace("not_line_ending", move |input: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + not_line_ending_::<_, _, true>(input) + } else { + not_line_ending_::<_, _, false>(input) + } + }) + .parse_next(input) +} + +fn not_line_ending_<I, E: ParserError<I>, const PARTIAL: bool>( + input: &mut I, +) -> PResult<<I as Stream>::Slice, E> +where + I: StreamIsPartial, + I: Stream, + I: Compare<&'static str>, + <I as Stream>::Token: AsChar + Clone, +{ + let res = take_till0(('\r', '\n')).parse_next(input)?; + if input.compare("\r") == CompareResult::Ok { + let comp = input.compare("\r\n"); + match comp { + //FIXME: calculate the right index + CompareResult::Ok => {} + CompareResult::Incomplete if PARTIAL && input.is_partial() => { + return Err(ErrMode::Incomplete(Needed::Unknown)); + } + CompareResult::Incomplete | CompareResult::Error => { + let e: ErrorKind = ErrorKind::Tag; + return Err(ErrMode::from_error_kind(input, e)); + } + } + } + Ok(res) +} + +/// Recognizes an end of line (both `"\n"` and `"\r\n"`). +/// +/// *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. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::line_ending; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// line_ending.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n"))); +/// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::line_ending; +/// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n"))); +/// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + I: Compare<&'static str>, +{ + trace("line_ending", alt(("\n", "\r\n"))).parse_next(input) +} + +/// Matches a newline character `'\n'`. +/// +/// *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. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::newline; +/// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> { +/// newline.parse_next(input) +/// } +/// +/// 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)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # 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("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +#[inline(always)] +pub fn newline<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, +{ + trace("newline", '\n'.map(AsChar::as_char)).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. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::tab; +/// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> { +/// tab.parse_next(input) +/// } +/// +/// 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)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # 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("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +#[inline(always)] +pub fn tab<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, +{ + trace("tab", '\t'.map(AsChar::as_char)).parse_next(input) +} + +/// Recognizes zero or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'` +/// +/// *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, +/// or if no terminating token is found (a non alphabetic character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::alpha0; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// alpha0.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("ab1c"), Ok(("1c", "ab"))); +/// assert_eq!(parser.parse_peek("1c"), Ok(("1c", ""))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::alpha0; +/// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("ab1c")), Ok((Partial::new("1c"), "ab"))); +/// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("1c")), Ok((Partial::new("1c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("alpha0", take_while(0.., AsChar::is_alpha)).parse_next(input) +} + +/// Recognizes one or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'` +/// +/// *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, +/// or if no terminating token is found (a non alphabetic character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::alpha1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// alpha1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("aB1c"), Ok(("1c", "aB"))); +/// assert_eq!(parser.parse_peek("1c"), Err(ErrMode::Backtrack(InputError::new("1c", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::alpha1; +/// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("aB1c")), Ok((Partial::new("1c"), "aB"))); +/// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("1c")), Err(ErrMode::Backtrack(InputError::new(Partial::new("1c"), ErrorKind::Slice)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("alpha1", take_while(1.., AsChar::is_alpha)).parse_next(input) +} + +/// Recognizes zero or more ASCII numerical characters: `'0'..='9'` +/// +/// *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, +/// or if no terminating token is found (a non digit character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::digit0; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// digit0.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21"))); +/// assert_eq!(parser.parse_peek("21"), Ok(("", "21"))); +/// assert_eq!(parser.parse_peek("a21c"), Ok(("a21c", ""))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::digit0; +/// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21"))); +/// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("a21c")), Ok((Partial::new("a21c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("digit0", take_while(0.., AsChar::is_dec_digit)).parse_next(input) +} + +/// Recognizes one or more ASCII numerical characters: `'0'..='9'` +/// +/// *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, +/// or if no terminating token is found (a non digit character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::digit1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// digit1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21"))); +/// assert_eq!(parser.parse_peek("c1"), Err(ErrMode::Backtrack(InputError::new("c1", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::digit1; +/// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21"))); +/// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("c1")), Err(ErrMode::Backtrack(InputError::new(Partial::new("c1"), ErrorKind::Slice)))); +/// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +/// +/// ## Parsing an integer +/// +/// You can use `digit1` in combination with [`Parser::try_map`] to parse an integer: +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed, Parser}; +/// # use winnow::ascii::digit1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<u32, InputError<&'s str>> { +/// digit1.try_map(str::parse).parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("416"), Ok(("", 416))); +/// assert_eq!(parser.parse_peek("12b"), Ok(("b", 12))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("digit1", take_while(1.., AsChar::is_dec_digit)).parse_next(input) +} + +/// Recognizes zero or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`, +/// `'a'..='f'` +/// +/// *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, +/// or if no terminating token is found (a non hexadecimal digit character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::hex_digit0; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// hex_digit0.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c"))); +/// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", ""))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::hex_digit0; +/// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c"))); +/// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("hex_digit0", take_while(0.., AsChar::is_hex_digit)).parse_next(input) +} + +/// Recognizes one or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`, +/// `'a'..='f'` +/// +/// *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, +/// or if no terminating token is found (a non hexadecimal digit character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::hex_digit1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// hex_digit1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c"))); +/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::hex_digit1; +/// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c"))); +/// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("hex_digit1", take_while(1.., AsChar::is_hex_digit)).parse_next(input) +} + +/// Recognizes zero or more octal characters: `'0'..='7'` +/// +/// *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, +/// or if no terminating token is found (a non octal digit character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::oct_digit0; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// oct_digit0.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21"))); +/// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", ""))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::oct_digit0; +/// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21"))); +/// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("oct_digit0", take_while(0.., AsChar::is_oct_digit)).parse_next(input) +} + +/// Recognizes one or more octal characters: `'0'..='7'` +/// +/// *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, +/// or if no terminating token is found (a non octal digit character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::oct_digit1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// oct_digit1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21"))); +/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::oct_digit1; +/// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21"))); +/// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("oct_digit0", take_while(1.., AsChar::is_oct_digit)).parse_next(input) +} + +/// Recognizes zero or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` +/// +/// *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, +/// or if no terminating token is found (a non alphanumerical character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::alphanumeric0; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// alphanumeric0.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ"))); +/// assert_eq!(parser.parse_peek("&Z21c"), Ok(("&Z21c", ""))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::alphanumeric0; +/// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ"))); +/// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("&Z21c")), Ok((Partial::new("&Z21c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("alphanumeric0", take_while(0.., AsChar::is_alphanum)).parse_next(input) +} + +/// Recognizes one or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` +/// +/// *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, +/// or if no terminating token is found (a non alphanumerical character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::alphanumeric1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// alphanumeric1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ"))); +/// assert_eq!(parser.parse_peek("&H2"), Err(ErrMode::Backtrack(InputError::new("&H2", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::alphanumeric1; +/// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ"))); +/// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("&H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("&H2"), ErrorKind::Slice)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar, +{ + trace("alphanumeric1", take_while(1.., AsChar::is_alphanum)).parse_next(input) +} + +/// Recognizes zero 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, +/// or if no terminating token is found (a non space character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::space0; +/// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t"))); +/// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, +{ + trace("space0", take_while(0.., AsChar::is_space)).parse_next(input) +} + +/// Recognizes zero 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, +/// or if no terminating token is found (a non space character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::space1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// space1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek(" \t21c"), Ok(("21c", " \t"))); +/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::space1; +/// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t"))); +/// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, +{ + trace("space1", take_while(1.., AsChar::is_space)).parse_next(input) +} + +/// Recognizes zero or more spaces, tabs, carriage returns and line feeds. +/// +/// *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, +/// or if no terminating token is found (a non space character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::ascii::multispace0; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// multispace0.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r"))); +/// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", ""))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::multispace0; +/// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r"))); +/// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, +{ + trace("multispace0", take_while(0.., (' ', '\t', '\r', '\n'))).parse_next(input) +} + +/// Recognizes one or more spaces, tabs, carriage returns and line feeds. +/// +/// *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, +/// or if no terminating token is found (a non space character). +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::ascii::multispace1; +/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// multispace1.parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r"))); +/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::ascii::multispace1; +/// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r"))); +/// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, +{ + trace("multispace1", take_while(1.., (' ', '\t', '\r', '\n'))).parse_next(input) +} + +/// Decode a decimal unsigned integer (e.g. [`u32`]) +/// +/// *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. +#[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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, + O: Uint, +{ + 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) + } + }) + .parse_next(input) +} + +/// Metadata for parsing unsigned integers, see [`dec_uint`] +pub trait Uint: Default { + #[doc(hidden)] + fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>; + #[doc(hidden)] + fn checked_add(self, by: u8, _: sealed::SealedMarker) -> 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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +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) + } +} + +/// Decode a decimal signed integer (e.g. [`i32`]) +/// +/// *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. +#[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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, + O: Int, +{ + 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) + } + }) + .parse_next(input) +} + +/// Metadata for parsing signed integers, see [`dec_int`] +pub trait Int: Uint { + #[doc(hidden)] + fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>; +} + +impl Int for i8 { + fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { + self.checked_sub(by as Self) + } +} + +impl Int for i16 { + fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { + self.checked_sub(by as Self) + } +} + +impl Int for i32 { + fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { + self.checked_sub(by as Self) + } +} + +impl Int for i64 { + fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { + self.checked_sub(by as Self) + } +} + +impl Int for i128 { + fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> { + self.checked_sub(by as Self) + } +} + +/// Decode a variable-width hexadecimal integer (e.g. [`u32`]) +/// +/// *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 +/// is hit before a hard boundary (non-hex character, more characters than supported). +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// use winnow::ascii::hex_uint; +/// +/// fn parser<'s>(s: &mut &'s [u8]) -> PResult<u32, InputError<&'s [u8]>> { +/// hex_uint(s) +/// } +/// +/// assert_eq!(parser.parse_peek(&b"01AE"[..]), Ok((&b""[..], 0x01AE))); +/// assert_eq!(parser.parse_peek(&b"abc"[..]), Ok((&b""[..], 0x0ABC))); +/// assert_eq!(parser.parse_peek(&b"ggg"[..]), Err(ErrMode::Backtrack(InputError::new(&b"ggg"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// use winnow::ascii::hex_uint; +/// +/// fn parser<'s>(s: &mut Partial<&'s [u8]>) -> PResult<u32, InputError<Partial<&'s [u8]>>> { +/// hex_uint(s) +/// } +/// +/// assert_eq!(parser.parse_peek(Partial::new(&b"01AE;"[..])), Ok((Partial::new(&b";"[..]), 0x01AE))); +/// assert_eq!(parser.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + O: HexUint, + <I as Stream>::Token: AsChar, + <I as Stream>::Slice: AsBStr, +{ + trace("hex_uint", move |input: &mut I| { + 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_offset = input.offset_at(max_nibbles); + let offset = match max_offset { + Ok(max_offset) => { + if max_offset < invalid_offset { + // Overflow + return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); + } else { + invalid_offset + } + } + Err(_) => { + if <I as StreamIsPartial>::is_partial_supported() + && input.is_partial() + && invalid_offset == input.eof_offset() + { + // Only the next byte is guaranteed required + return Err(ErrMode::Incomplete(Needed::new(1))); + } else { + invalid_offset + } + } + }; + if offset == 0 { + // Must be at least one digit + return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); + } + let parsed = input.next_slice(offset); + + let mut res = O::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; + } + + Ok(res) + }) + .parse_next(input) +} + +/// Metadata for parsing hex numbers, see [`hex_uint`] +pub trait HexUint: + Default + Shl<Self, Output = Self> + Add<Self, Output = Self> + From<u8> +{ + #[doc(hidden)] + fn max_nibbles(_: sealed::SealedMarker) -> usize; +} + +impl HexUint for u8 { + #[inline(always)] + fn max_nibbles(_: sealed::SealedMarker) -> usize { + 2 + } +} + +impl HexUint for u16 { + #[inline(always)] + fn max_nibbles(_: sealed::SealedMarker) -> usize { + 4 + } +} + +impl HexUint for u32 { + #[inline(always)] + fn max_nibbles(_: sealed::SealedMarker) -> usize { + 8 + } +} + +impl HexUint for u64 { + #[inline(always)] + fn max_nibbles(_: sealed::SealedMarker) -> usize { + 16 + } +} + +impl HexUint for u128 { + #[inline(always)] + fn max_nibbles(_: sealed::SealedMarker) -> usize { + 32 + } +} + +/// Recognizes floating point number in text format and returns a [`f32`] or [`f64`]. +/// +/// *Complete version*: Can parse until the end of input. +/// +/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::error::Needed::Size; +/// use winnow::ascii::float; +/// +/// fn parser<'s>(s: &mut &'s str) -> PResult<f64, InputError<&'s str>> { +/// float(s) +/// } +/// +/// assert_eq!(parser.parse_peek("11e-1"), Ok(("", 1.1))); +/// assert_eq!(parser.parse_peek("123E-02"), Ok(("", 1.23))); +/// assert_eq!(parser.parse_peek("123K-01"), Ok(("K-01", 123.0))); +/// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Tag)))); +/// ``` +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::ascii::float; +/// +/// fn parser<'s>(s: &mut Partial<&'s str>) -> PResult<f64, InputError<Partial<&'s str>>> { +/// float(s) +/// } +/// +/// assert_eq!(parser.parse_peek(Partial::new("11e-1 ")), Ok((Partial::new(" "), 1.1))); +/// assert_eq!(parser.parse_peek(Partial::new("11e-1")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(parser.parse_peek(Partial::new("123E-02")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(parser.parse_peek(Partial::new("123K-01")), Ok((Partial::new("K-01"), 123.0))); +/// assert_eq!(parser.parse_peek(Partial::new("abc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("abc"), ErrorKind::Tag)))); +/// ``` +#[inline(always)] +#[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> +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, +{ + trace("float", move |input: &mut I| { + let s = recognize_float_or_exceptions(input)?; + s.parse_slice() + .ok_or_else(|| ErrMode::from_error_kind(input, ErrorKind::Verify)) + }) + .parse_next(input) +} + +#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug +fn recognize_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 as Stream>::Token: AsChar + Clone, + <I as Stream>::IterOffsets: Clone, + I: AsBStr, +{ + alt(( + recognize_float, + crate::token::tag_no_case("nan"), + crate::token::tag_no_case("infinity"), + crate::token::tag_no_case("inf"), + )) + .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> +where + I: StreamIsPartial, + I: Stream, + I: Compare<&'static str>, + <I as Stream>::Token: AsChar + Clone, + <I as Stream>::IterOffsets: Clone, + I: AsBStr, +{ + ( + opt(one_of(['+', '-'])), + alt(( + (digit1, opt(('.', opt(digit1)))).map(|_| ()), + ('.', digit1).map(|_| ()), + )), + opt((one_of(['e', 'E']), opt(one_of(['+', '-'])), cut_err(digit1))), + ) + .recognize() + .parse_next(input) +} + +/// Matches a byte string 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 +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult}; +/// # use winnow::ascii::digit1; +/// # use winnow::prelude::*; +/// use winnow::ascii::escaped; +/// use winnow::token::one_of; +/// +/// fn esc(s: &str) -> IResult<&str, &str> { +/// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s) +/// } +/// +/// assert_eq!(esc("123;"), Ok((";", "123"))); +/// assert_eq!(esc(r#"12\"34;"#), Ok((";", r#"12\"34"#))); +/// ``` +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult}; +/// # use winnow::ascii::digit1; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::ascii::escaped; +/// use winnow::token::one_of; +/// +/// fn esc(s: Partial<&str>) -> IResult<Partial<&str>, &str> { +/// 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, + control_char: char, + mut escapable: G, +) -> impl Parser<I, <I 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>, +{ + trace("escaped", move |input: &mut I| { + if <I 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) + } + }) +} + +fn streaming_escaped_internal<I, Error, F, G, O1, O2>( + input: &mut I, + normal: &mut F, + control_char: char, + escapable: &mut G, +) -> PResult<<I 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>, +{ + let start = input.checkpoint(); + + while input.eof_offset() > 0 { + let current_len = input.eof_offset(); + + match opt(normal.by_ref()).parse_next(input)? { + Some(_) => { + if input.eof_offset() == current_len { + let offset = input.offset_from(&start); + input.reset(start); + return Ok(input.next_slice(offset)); + } + } + None => { + if opt(control_char).parse_next(input)?.is_some() { + let _ = escapable.parse_next(input)?; + } else { + let offset = input.offset_from(&start); + input.reset(start); + return Ok(input.next_slice(offset)); + } + } + } + } + + Err(ErrMode::Incomplete(Needed::Unknown)) +} + +fn complete_escaped_internal<'a, I: 'a, Error, F, G, O1, O2>( + input: &mut I, + normal: &mut F, + control_char: char, + escapable: &mut G, +) -> PResult<<I as Stream>::Slice, Error> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: crate::stream::AsChar + Clone, + F: Parser<I, O1, Error>, + G: Parser<I, O2, Error>, + Error: ParserError<I>, +{ + let start = input.checkpoint(); + + while input.eof_offset() > 0 { + let current_len = input.eof_offset(); + + match opt(normal.by_ref()).parse_next(input)? { + Some(_) => { + if input.eof_offset() == current_len { + let offset = input.offset_from(&start); + input.reset(start); + return Ok(input.next_slice(offset)); + } + } + None => { + if opt(control_char).parse_next(input)?.is_some() { + let _ = escapable.parse_next(input)?; + } else { + let offset = input.offset_from(&start); + input.reset(start); + return Ok(input.next_slice(offset)); + } + } + } + } + + input.reset(start); + Ok(input.finish()) +} + +/// Matches a byte string with escaped characters. +/// +/// * 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 +/// +/// As an example, the chain `abc\tdef` could be `abc def` (it also consumes the control character) +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use std::str::from_utf8; +/// use winnow::token::tag; +/// use winnow::ascii::escaped_transform; +/// use winnow::ascii::alpha1; +/// use winnow::combinator::alt; +/// +/// fn parser<'s>(input: &mut &'s str) -> PResult<String, InputError<&'s str>> { +/// escaped_transform( +/// alpha1, +/// '\\', +/// alt(( +/// "\\".value("\\"), +/// "\"".value("\""), +/// "n".value("\n"), +/// )) +/// ).parse_next(input) +/// } +/// +/// assert_eq!(parser.parse_peek("ab\\\"cd"), Ok(("", String::from("ab\"cd")))); +/// assert_eq!(parser.parse_peek("ab\\ncd"), Ok(("", String::from("ab\ncd")))); +/// ``` +/// +/// ``` +/// # 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::ascii::escaped_transform; +/// use winnow::ascii::alpha1; +/// use winnow::combinator::alt; +/// +/// fn parser<'s>(input: &mut Partial<&'s str>) -> PResult<String, InputError<Partial<&'s str>>> { +/// escaped_transform( +/// alpha1, +/// '\\', +/// alt(( +/// "\\".value("\\"), +/// "\"".value("\""), +/// "n".value("\n"), +/// )) +/// ).parse_next(input) +/// } +/// +/// 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, + control_char: char, + mut transform: G, +) -> impl Parser<I, 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>, +{ + 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) + } else { + complete_escaped_transform_internal(input, &mut normal, control_char, &mut transform) + } + }) +} + +fn streaming_escaped_transform_internal<I, Error, F, G, Output>( + input: &mut I, + normal: &mut F, + control_char: char, + transform: &mut G, +) -> PResult<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>, +{ + let mut res = Output::initial(Some(input.eof_offset())); + + while input.eof_offset() > 0 { + let current_len = input.eof_offset(); + match opt(normal.by_ref()).parse_next(input)? { + Some(o) => { + res.accumulate(o); + if input.eof_offset() == current_len { + return Ok(res); + } + } + None => { + if opt(control_char).parse_next(input)?.is_some() { + let o = transform.parse_next(input)?; + res.accumulate(o); + } else { + return Ok(res); + } + } + } + } + Err(ErrMode::Incomplete(Needed::Unknown)) +} + +fn complete_escaped_transform_internal<I, Error, F, G, Output>( + input: &mut I, + normal: &mut F, + control_char: char, + transform: &mut G, +) -> PResult<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>, +{ + let mut res = Output::initial(Some(input.eof_offset())); + + while input.eof_offset() > 0 { + let current_len = input.eof_offset(); + + match opt(normal.by_ref()).parse_next(input)? { + Some(o) => { + res.accumulate(o); + if input.eof_offset() == current_len { + return Ok(res); + } + } + None => { + if opt(control_char).parse_next(input)?.is_some() { + let o = transform.parse_next(input)?; + res.accumulate(o); + } else { + return Ok(res); + } + } + } + } + Ok(res) +} + +mod sealed { + pub struct SealedMarker; +} diff --git a/vendor/winnow/src/ascii/tests.rs b/vendor/winnow/src/ascii/tests.rs new file mode 100644 index 0000000..aacbd86 --- /dev/null +++ b/vendor/winnow/src/ascii/tests.rs @@ -0,0 +1,1554 @@ +use super::*; +use crate::prelude::*; + +mod complete { + use super::*; + use crate::combinator::alt; + use crate::combinator::opt; + use crate::error::ErrMode; + use crate::error::ErrorKind; + use crate::error::InputError; + use crate::stream::ParseSlice; + use crate::token::none_of; + use crate::token::one_of; + #[cfg(feature = "alloc")] + use crate::{lib::std::string::String, lib::std::vec::Vec}; + use proptest::prelude::*; + + macro_rules! assert_parse( + ($left: expr, $right: expr) => { + let res: $crate::IResult<_, _, InputError<_>> = $left; + assert_eq!(res, $right); + }; + ); + + #[test] + fn character() { + let empty: &[u8] = b""; + let a: &[u8] = b"abcd"; + let b: &[u8] = b"1234"; + let c: &[u8] = b"a123"; + let d: &[u8] = "azé12".as_bytes(); + let e: &[u8] = b" "; + let f: &[u8] = b" ;"; + //assert_eq!(alpha1::<_, InputError>(a), Err(ErrMode::Incomplete(Needed::Size(1)))); + assert_parse!(alpha1.parse_peek(a), Ok((empty, a))); + assert_eq!( + alpha1.parse_peek(b), + Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Slice))) + ); + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(c), + Ok((&c[1..], &b"a"[..])) + ); + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(d), + Ok(("é12".as_bytes(), &b"az"[..])) + ); + assert_eq!( + digit1.parse_peek(a), + Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) + ); + assert_eq!(digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); + assert_eq!( + digit1.parse_peek(c), + Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) + ); + assert_eq!( + digit1.parse_peek(d), + Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) + ); + assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(a), Ok((empty, a))); + assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); + assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(c), Ok((empty, c))); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(d), + Ok(("zé12".as_bytes(), &b"a"[..])) + ); + assert_eq!( + hex_digit1.parse_peek(e), + Err(ErrMode::Backtrack(InputError::new(e, ErrorKind::Slice))) + ); + assert_eq!( + oct_digit1.parse_peek(a), + Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) + ); + assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); + assert_eq!( + oct_digit1.parse_peek(c), + Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) + ); + assert_eq!( + oct_digit1.parse_peek(d), + Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(a), + Ok((empty, a)) + ); + //assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b))); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(c), + Ok((empty, c)) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(d), + Ok(("é12".as_bytes(), &b"az"[..])) + ); + assert_eq!(space1::<_, InputError<_>>.parse_peek(e), Ok((empty, e))); + assert_eq!( + space1::<_, InputError<_>>.parse_peek(f), + Ok((&b";"[..], &b" "[..])) + ); + } + + #[cfg(feature = "alloc")] + #[test] + fn character_s() { + let empty = ""; + let a = "abcd"; + let b = "1234"; + let c = "a123"; + let d = "azé12"; + let e = " "; + assert_eq!(alpha1::<_, InputError<_>>.parse_peek(a), Ok((empty, a))); + assert_eq!( + alpha1.parse_peek(b), + Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Slice))) + ); + assert_eq!(alpha1::<_, InputError<_>>.parse_peek(c), Ok((&c[1..], "a"))); + assert_eq!(alpha1::<_, InputError<_>>.parse_peek(d), Ok(("é12", "az"))); + assert_eq!( + digit1.parse_peek(a), + Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) + ); + assert_eq!(digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); + assert_eq!( + digit1.parse_peek(c), + Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) + ); + assert_eq!( + digit1.parse_peek(d), + Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) + ); + assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(a), Ok((empty, a))); + assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); + assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(c), Ok((empty, c))); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(d), + Ok(("zé12", "a")) + ); + assert_eq!( + hex_digit1.parse_peek(e), + Err(ErrMode::Backtrack(InputError::new(e, ErrorKind::Slice))) + ); + assert_eq!( + oct_digit1.parse_peek(a), + Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) + ); + assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); + assert_eq!( + oct_digit1.parse_peek(c), + Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) + ); + assert_eq!( + oct_digit1.parse_peek(d), + Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(a), + Ok((empty, a)) + ); + //assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b))); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(c), + Ok((empty, c)) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(d), + Ok(("é12", "az")) + ); + assert_eq!(space1::<_, InputError<_>>.parse_peek(e), Ok((empty, e))); + } + + use crate::stream::Offset; + #[test] + fn offset() { + let a = &b"abcd;"[..]; + let b = &b"1234;"[..]; + let c = &b"a123;"[..]; + let d = &b" \t;"[..]; + let e = &b" \t\r\n;"[..]; + let f = &b"123abcDEF;"[..]; + + match alpha1::<_, InputError<_>>.parse_peek(a) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&a) + i.len(), a.len()); + } + _ => panic!("wrong return type in offset test for alpha"), + } + match digit1::<_, InputError<_>>.parse_peek(b) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&b) + i.len(), b.len()); + } + _ => panic!("wrong return type in offset test for digit"), + } + match alphanumeric1::<_, InputError<_>>.parse_peek(c) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&c) + i.len(), c.len()); + } + _ => panic!("wrong return type in offset test for alphanumeric"), + } + match space1::<_, InputError<_>>.parse_peek(d) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&d) + i.len(), d.len()); + } + _ => panic!("wrong return type in offset test for space"), + } + match multispace1::<_, InputError<_>>.parse_peek(e) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&e) + i.len(), e.len()); + } + _ => panic!("wrong return type in offset test for multispace"), + } + match hex_digit1::<_, InputError<_>>.parse_peek(f) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&f) + i.len(), f.len()); + } + _ => panic!("wrong return type in offset test for hex_digit"), + } + match oct_digit1::<_, InputError<_>>.parse_peek(f) { + Ok((i, _)) => { + assert_eq!(i.offset_from(&f) + i.len(), f.len()); + } + _ => panic!("wrong return type in offset test for oct_digit"), + } + } + + #[test] + fn is_not_line_ending_bytes() { + let a: &[u8] = b"ab12cd\nefgh"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(a), + Ok((&b"\nefgh"[..], &b"ab12cd"[..])) + ); + + let b: &[u8] = b"ab12cd\nefgh\nijkl"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(b), + Ok((&b"\nefgh\nijkl"[..], &b"ab12cd"[..])) + ); + + let c: &[u8] = b"ab12cd\r\nefgh\nijkl"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(c), + Ok((&b"\r\nefgh\nijkl"[..], &b"ab12cd"[..])) + ); + + let d: &[u8] = b"ab12cd"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(d), + Ok((&[][..], d)) + ); + } + + #[test] + fn is_not_line_ending_str() { + let f = "βèƒôřè\rÂßÇáƒƭèř"; + assert_eq!( + not_line_ending.parse_peek(f), + Err(ErrMode::Backtrack(InputError::new( + &f[12..], + ErrorKind::Tag + ))) + ); + + let g2: &str = "ab12cd"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(g2), + Ok(("", g2)) + ); + } + + #[test] + fn hex_digit_test() { + let i = &b"0123456789abcdefABCDEF;"[..]; + assert_parse!(hex_digit1.parse_peek(i), Ok((&b";"[..], &i[..i.len() - 1]))); + + let i = &b"g"[..]; + assert_parse!( + hex_digit1.parse_peek(i), + Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice))) + ); + + let i = &b"G"[..]; + assert_parse!( + hex_digit1.parse_peek(i), + Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice))) + ); + + assert!(AsChar::is_hex_digit(b'0')); + assert!(AsChar::is_hex_digit(b'9')); + assert!(AsChar::is_hex_digit(b'a')); + assert!(AsChar::is_hex_digit(b'f')); + assert!(AsChar::is_hex_digit(b'A')); + assert!(AsChar::is_hex_digit(b'F')); + assert!(!AsChar::is_hex_digit(b'g')); + assert!(!AsChar::is_hex_digit(b'G')); + assert!(!AsChar::is_hex_digit(b'/')); + assert!(!AsChar::is_hex_digit(b':')); + assert!(!AsChar::is_hex_digit(b'@')); + assert!(!AsChar::is_hex_digit(b'\x60')); + } + + #[test] + fn oct_digit_test() { + let i = &b"01234567;"[..]; + assert_parse!(oct_digit1.parse_peek(i), Ok((&b";"[..], &i[..i.len() - 1]))); + + let i = &b"8"[..]; + assert_parse!( + oct_digit1.parse_peek(i), + Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice))) + ); + + assert!(AsChar::is_oct_digit(b'0')); + assert!(AsChar::is_oct_digit(b'7')); + assert!(!AsChar::is_oct_digit(b'8')); + assert!(!AsChar::is_oct_digit(b'9')); + assert!(!AsChar::is_oct_digit(b'a')); + assert!(!AsChar::is_oct_digit(b'A')); + assert!(!AsChar::is_oct_digit(b'/')); + assert!(!AsChar::is_oct_digit(b':')); + assert!(!AsChar::is_oct_digit(b'@')); + assert!(!AsChar::is_oct_digit(b'\x60')); + } + + #[test] + fn full_line_windows() { + fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { + (not_line_ending, line_ending).parse_peek(i) + } + let input = b"abc\r\n"; + let output = take_full_line(input); + assert_eq!(output, Ok((&b""[..], (&b"abc"[..], &b"\r\n"[..])))); + } + + #[test] + fn full_line_unix() { + fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { + (not_line_ending, line_ending).parse_peek(i) + } + let input = b"abc\n"; + let output = take_full_line(input); + assert_eq!(output, Ok((&b""[..], (&b"abc"[..], &b"\n"[..])))); + } + + #[test] + fn check_windows_lineending() { + let input = b"\r\n"; + let output = line_ending.parse_peek(&input[..]); + assert_parse!(output, Ok((&b""[..], &b"\r\n"[..]))); + } + + #[test] + fn check_unix_lineending() { + let input = b"\n"; + let output = line_ending.parse_peek(&input[..]); + assert_parse!(output, Ok((&b""[..], &b"\n"[..]))); + } + + #[test] + fn cr_lf() { + assert_parse!( + crlf.parse_peek(&b"\r\na"[..]), + Ok((&b"a"[..], &b"\r\n"[..])) + ); + assert_parse!( + crlf.parse_peek(&b"\r"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b"\r"[..], + ErrorKind::Tag + ))) + ); + assert_parse!( + crlf.parse_peek(&b"\ra"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b"\ra"[..], + ErrorKind::Tag + ))) + ); + + assert_parse!(crlf.parse_peek("\r\na"), Ok(("a", "\r\n"))); + assert_parse!( + crlf.parse_peek("\r"), + Err(ErrMode::Backtrack(error_position!(&"\r", ErrorKind::Tag))) + ); + assert_parse!( + crlf.parse_peek("\ra"), + Err(ErrMode::Backtrack(error_position!(&"\ra", ErrorKind::Tag))) + ); + } + + #[test] + fn end_of_line() { + assert_parse!( + line_ending.parse_peek(&b"\na"[..]), + Ok((&b"a"[..], &b"\n"[..])) + ); + assert_parse!( + line_ending.parse_peek(&b"\r\na"[..]), + Ok((&b"a"[..], &b"\r\n"[..])) + ); + assert_parse!( + line_ending.parse_peek(&b"\r"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b"\r"[..], + ErrorKind::Tag + ))) + ); + assert_parse!( + line_ending.parse_peek(&b"\ra"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b"\ra"[..], + ErrorKind::Tag + ))) + ); + + assert_parse!(line_ending.parse_peek("\na"), Ok(("a", "\n"))); + assert_parse!(line_ending.parse_peek("\r\na"), Ok(("a", "\r\n"))); + assert_parse!( + line_ending.parse_peek("\r"), + Err(ErrMode::Backtrack(error_position!(&"\r", ErrorKind::Tag))) + ); + assert_parse!( + line_ending.parse_peek("\ra"), + Err(ErrMode::Backtrack(error_position!(&"\ra", ErrorKind::Tag))) + ); + } + + 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)), + } + } + + 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)), + } + } + + 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); + } + } + + #[test] + fn hex_uint_tests() { + fn hex_u32(input: &[u8]) -> IResult<&[u8], u32> { + hex_uint.parse_peek(input) + } + + assert_parse!( + hex_u32(&b";"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b";"[..], + ErrorKind::Slice + ))) + ); + assert_parse!(hex_u32(&b"ff;"[..]), Ok((&b";"[..], 255))); + assert_parse!(hex_u32(&b"1be2;"[..]), Ok((&b";"[..], 7138))); + assert_parse!(hex_u32(&b"c5a31be2;"[..]), Ok((&b";"[..], 3_315_801_058))); + assert_parse!(hex_u32(&b"C5A31be2;"[..]), Ok((&b";"[..], 3_315_801_058))); + assert_parse!( + hex_u32(&b"00c5a31be2;"[..]), // overflow + Err(ErrMode::Backtrack(error_position!( + &&b"00c5a31be2;"[..], + ErrorKind::Verify + ))) + ); + assert_parse!( + hex_u32(&b"c5a31be201;"[..]), // overflow + Err(ErrMode::Backtrack(error_position!( + &&b"c5a31be201;"[..], + ErrorKind::Verify + ))) + ); + assert_parse!(hex_u32(&b"ffffffff;"[..]), Ok((&b";"[..], 4_294_967_295))); + assert_parse!( + hex_u32(&b"ffffffffffffffff;"[..]), // overflow + Err(ErrMode::Backtrack(error_position!( + &&b"ffffffffffffffff;"[..], + ErrorKind::Verify + ))) + ); + assert_parse!( + hex_u32(&b"ffffffffffffffff"[..]), // overflow + Err(ErrMode::Backtrack(error_position!( + &&b"ffffffffffffffff"[..], + ErrorKind::Verify + ))) + ); + assert_parse!(hex_u32(&b"0x1be2;"[..]), Ok((&b"x1be2;"[..], 0))); + assert_parse!(hex_u32(&b"12af"[..]), Ok((&b""[..], 0x12af))); + } + + #[test] + #[cfg(feature = "std")] + fn float_test() { + let mut test_cases = vec![ + "+3.14", + "3.14", + "-3.14", + "0", + "0.0", + "1.", + ".789", + "-.5", + "1e7", + "-1E-7", + ".3e-2", + "1.e4", + "1.2e4", + "12.34", + "-1.234E-12", + "-1.234e-12", + "0.00000000000000000087", + ]; + + for test in test_cases.drain(..) { + let expected32 = str::parse::<f32>(test).unwrap(); + let expected64 = str::parse::<f64>(test).unwrap(); + + println!("now parsing: {} -> {}", test, expected32); + + let larger = test.to_string(); + + assert_parse!( + float.parse_peek(larger.as_bytes()), + Ok((&b""[..], expected32)) + ); + assert_parse!(float.parse_peek(&larger[..]), Ok(("", expected32))); + + assert_parse!( + float.parse_peek(larger.as_bytes()), + Ok((&b""[..], expected64)) + ); + assert_parse!(float.parse_peek(&larger[..]), Ok(("", expected64))); + } + + let remaining_exponent = "-1.234E-"; + assert_parse!( + float::<_, f64, _>.parse_peek(remaining_exponent), + Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice))) + ); + + let (i, nan) = float::<_, f32, ()>.parse_peek("NaN").unwrap(); + assert!(nan.is_nan()); + assert_eq!(i, ""); + + let (i, inf) = float::<_, f32, ()>.parse_peek("inf").unwrap(); + assert!(inf.is_infinite()); + assert_eq!(i, ""); + let (i, inf) = float::<_, f32, ()>.parse_peek("infinity").unwrap(); + assert!(inf.is_infinite()); + assert_eq!(i, ""); + } + + #[cfg(feature = "std")] + fn parse_f64(i: &str) -> IResult<&str, f64, ()> { + match super::recognize_float_or_exceptions.parse_peek(i) { + Err(e) => Err(e), + Ok((i, s)) => { + if s.is_empty() { + return Err(ErrMode::Backtrack(())); + } + match s.parse_slice() { + Some(n) => Ok((i, n)), + None => Err(ErrMode::Backtrack(())), + } + } + } + } + + proptest! { + #[test] + #[cfg(feature = "std")] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn floats(s in "\\PC*") { + 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" + #[test] + fn complete_escaped_hang() { + // issue #1336 "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) + } + + escaped_string("7").unwrap(); + escaped_string("a7").unwrap(); + } + + #[test] + fn complete_escaped_hang_1118() { + // issue ##1118 escaped does not work with empty string + fn unquote(input: &str) -> IResult<&str, &str> { + use crate::combinator::delimited; + use crate::combinator::opt; + use crate::token::one_of; + + delimited( + '"', + escaped( + opt(none_of(['\\', '"'])), + '\\', + one_of(['\\', '"', 'r', 'n', 't']), + ), + '"', + ) + .parse_peek(input) + } + + assert_eq!(unquote(r#""""#), Ok(("", ""))); + } + + #[cfg(feature = "alloc")] + #[allow(unused_variables)] + #[test] + fn complete_escaping() { + use crate::ascii::{alpha1 as alpha, digit1 as digit}; + use crate::token::one_of; + + fn esc(i: &[u8]) -> IResult<&[u8], &[u8]> { + 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"[..]))); + assert_eq!(esc(&b"\\\"abcd;"[..]), Ok((&b";"[..], &b"\\\"abcd"[..]))); + assert_eq!(esc(&b"\\n;"[..]), Ok((&b";"[..], &b"\\n"[..]))); + assert_eq!(esc(&b"ab\\\"12"[..]), Ok((&b"12"[..], &b"ab\\\""[..]))); + assert_eq!( + esc(&b"AB\\"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b""[..], + ErrorKind::Token + ))) + ); + assert_eq!( + esc(&b"AB\\A"[..]), + Err(ErrMode::Backtrack(error_node_position!( + &&b"AB\\A"[..], + ErrorKind::Token, + error_position!(&&b"A"[..], ErrorKind::Verify) + ))) + ); + + fn esc2(i: &[u8]) -> IResult<&[u8], &[u8]> { + escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) + } + assert_eq!(esc2(&b"12\\nnn34"[..]), Ok((&b"nn34"[..], &b"12\\n"[..]))); + } + + #[cfg(feature = "alloc")] + #[test] + fn complete_escaping_str() { + use crate::ascii::{alpha1 as alpha, digit1 as digit}; + use crate::token::one_of; + + fn esc(i: &str) -> IResult<&str, &str> { + escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) + } + assert_eq!(esc("abcd;"), Ok((";", "abcd"))); + assert_eq!(esc("ab\\\"cd;"), Ok((";", "ab\\\"cd"))); + assert_eq!(esc("\\\"abcd;"), Ok((";", "\\\"abcd"))); + assert_eq!(esc("\\n;"), Ok((";", "\\n"))); + assert_eq!(esc("ab\\\"12"), Ok(("12", "ab\\\""))); + assert_eq!( + esc("AB\\"), + Err(ErrMode::Backtrack(error_position!(&"", ErrorKind::Token))) + ); + assert_eq!( + esc("AB\\A"), + Err(ErrMode::Backtrack(error_node_position!( + &"AB\\A", + ErrorKind::Token, + error_position!(&"A", ErrorKind::Verify) + ))) + ); + + fn esc2(i: &str) -> IResult<&str, &str> { + 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) + } + assert_eq!(esc3("ab␛ncd;"), Ok((";", "ab␛ncd"))); + } + + #[test] + fn test_escaped_error() { + fn esc(s: &str) -> IResult<&str, &str> { + use crate::ascii::digit1; + escaped(digit1, '\\', one_of(['\"', 'n', '\\'])).parse_peek(s) + } + + assert_eq!(esc("abcd"), Ok(("abcd", ""))); + } + + #[cfg(feature = "alloc")] + #[test] + fn complete_escape_transform() { + use crate::ascii::alpha1 as alpha; + + #[cfg(feature = "alloc")] + fn to_s(i: Vec<u8>) -> String { + String::from_utf8_lossy(&i).into_owned() + } + + fn esc(i: &[u8]) -> IResult<&[u8], String> { + escaped_transform( + alpha, + '\\', + alt(( + "\\".value(&b"\\"[..]), + "\"".value(&b"\""[..]), + "n".value(&b"\n"[..]), + )), + ) + .map(to_s) + .parse_peek(i) + } + + assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], String::from("abcd")))); + assert_eq!( + esc(&b"ab\\\"cd;"[..]), + Ok((&b";"[..], String::from("ab\"cd"))) + ); + assert_eq!( + esc(&b"\\\"abcd;"[..]), + Ok((&b";"[..], String::from("\"abcd"))) + ); + assert_eq!(esc(&b"\\n;"[..]), Ok((&b";"[..], String::from("\n")))); + assert_eq!( + esc(&b"ab\\\"12"[..]), + Ok((&b"12"[..], String::from("ab\""))) + ); + assert_eq!( + esc(&b"AB\\"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b""[..], + ErrorKind::Tag + ))) + ); + assert_eq!( + esc(&b"AB\\A"[..]), + Err(ErrMode::Backtrack(error_node_position!( + &&b"AB\\A"[..], + ErrorKind::Eof, + error_position!(&&b"A"[..], ErrorKind::Tag) + ))) + ); + + fn esc2(i: &[u8]) -> IResult<&[u8], String> { + escaped_transform( + alpha, + '&', + alt(( + "egrave;".value("è".as_bytes()), + "agrave;".value("à".as_bytes()), + )), + ) + .map(to_s) + .parse_peek(i) + } + assert_eq!( + esc2(&b"abèDEF;"[..]), + Ok((&b";"[..], String::from("abèDEF"))) + ); + assert_eq!( + esc2(&b"abèDàEF;"[..]), + Ok((&b";"[..], String::from("abèDàEF"))) + ); + } + + #[cfg(feature = "std")] + #[test] + fn complete_escape_transform_str() { + use crate::ascii::alpha1 as alpha; + + fn esc(i: &str) -> IResult<&str, String> { + escaped_transform( + alpha, + '\\', + alt(("\\".value("\\"), "\"".value("\""), "n".value("\n"))), + ) + .parse_peek(i) + } + + assert_eq!(esc("abcd;"), Ok((";", String::from("abcd")))); + assert_eq!(esc("ab\\\"cd;"), Ok((";", String::from("ab\"cd")))); + assert_eq!(esc("\\\"abcd;"), Ok((";", String::from("\"abcd")))); + assert_eq!(esc("\\n;"), Ok((";", String::from("\n")))); + assert_eq!(esc("ab\\\"12"), Ok(("12", String::from("ab\"")))); + assert_eq!( + esc("AB\\"), + Err(ErrMode::Backtrack(error_position!(&"", ErrorKind::Tag))) + ); + assert_eq!( + esc("AB\\A"), + Err(ErrMode::Backtrack(error_node_position!( + &"AB\\A", + ErrorKind::Eof, + error_position!(&"A", ErrorKind::Tag) + ))) + ); + + fn esc2(i: &str) -> IResult<&str, String> { + escaped_transform( + alpha, + '&', + alt(("egrave;".value("è"), "agrave;".value("à"))), + ) + .parse_peek(i) + } + assert_eq!(esc2("abèDEF;"), Ok((";", String::from("abèDEF")))); + assert_eq!( + esc2("abèDàEF;"), + Ok((";", String::from("abèDàEF"))) + ); + + fn esc3(i: &str) -> IResult<&str, String> { + escaped_transform(alpha, '␛', alt(("0".value("\0"), "n".value("\n")))).parse_peek(i) + } + assert_eq!(esc3("a␛0bc␛n"), Ok(("", String::from("a\0bc\n")))); + } + + #[test] + #[cfg(feature = "alloc")] + fn test_escaped_transform_error() { + fn esc_trans(s: &str) -> IResult<&str, String> { + use crate::ascii::digit1; + escaped_transform(digit1, '\\', "n").parse_peek(s) + } + + assert_eq!(esc_trans("abcd"), Ok(("abcd", String::new()))); + } +} + +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) => { + let res: $crate::IResult<_, _, InputError<_>> = $left; + assert_eq!(res, $right); + }; + ); + + #[test] + fn character() { + let a: &[u8] = b"abcd"; + let b: &[u8] = b"1234"; + let c: &[u8] = b"a123"; + let d: &[u8] = "azé12".as_bytes(); + let e: &[u8] = b" "; + let f: &[u8] = b" ;"; + //assert_eq!(alpha1::<_, Error<_>>(a), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_parse!( + alpha1.parse_peek(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + alpha1.parse_peek(Partial::new(b)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(b), + ErrorKind::Slice + ))) + ); + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(Partial::new(c)), + Ok((Partial::new(&c[1..]), &b"a"[..])) + ); + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(Partial::new(d)), + Ok((Partial::new("é12".as_bytes()), &b"az"[..])) + ); + assert_eq!( + digit1.parse_peek(Partial::new(a)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(a), + ErrorKind::Slice + ))) + ); + assert_eq!( + digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + digit1.parse_peek(Partial::new(c)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(c), + ErrorKind::Slice + ))) + ); + assert_eq!( + digit1.parse_peek(Partial::new(d)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(d), + ErrorKind::Slice + ))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(c)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(d)), + Ok((Partial::new("zé12".as_bytes()), &b"a"[..])) + ); + assert_eq!( + hex_digit1.parse_peek(Partial::new(e)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(e), + ErrorKind::Slice + ))) + ); + assert_eq!( + oct_digit1.parse_peek(Partial::new(a)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(a), + ErrorKind::Slice + ))) + ); + assert_eq!( + oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + oct_digit1.parse_peek(Partial::new(c)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(c), + ErrorKind::Slice + ))) + ); + assert_eq!( + oct_digit1.parse_peek(Partial::new(d)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(d), + ErrorKind::Slice + ))) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + //assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b))); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(d)), + Ok((Partial::new("é12".as_bytes()), &b"az"[..])) + ); + assert_eq!( + space1::<_, InputError<_>>.parse_peek(Partial::new(e)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + space1::<_, InputError<_>>.parse_peek(Partial::new(f)), + Ok((Partial::new(&b";"[..]), &b" "[..])) + ); + } + + #[cfg(feature = "alloc")] + #[test] + fn character_s() { + let a = "abcd"; + let b = "1234"; + let c = "a123"; + let d = "azé12"; + let e = " "; + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + alpha1.parse_peek(Partial::new(b)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(b), + ErrorKind::Slice + ))) + ); + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(Partial::new(c)), + Ok((Partial::new(&c[1..]), "a")) + ); + assert_eq!( + alpha1::<_, InputError<_>>.parse_peek(Partial::new(d)), + Ok((Partial::new("é12"), "az")) + ); + assert_eq!( + digit1.parse_peek(Partial::new(a)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(a), + ErrorKind::Slice + ))) + ); + assert_eq!( + digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + digit1.parse_peek(Partial::new(c)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(c), + ErrorKind::Slice + ))) + ); + assert_eq!( + digit1.parse_peek(Partial::new(d)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(d), + ErrorKind::Slice + ))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(c)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(d)), + Ok((Partial::new("zé12"), "a")) + ); + assert_eq!( + hex_digit1.parse_peek(Partial::new(e)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(e), + ErrorKind::Slice + ))) + ); + assert_eq!( + oct_digit1.parse_peek(Partial::new(a)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(a), + ErrorKind::Slice + ))) + ); + assert_eq!( + oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + oct_digit1.parse_peek(Partial::new(c)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(c), + ErrorKind::Slice + ))) + ); + assert_eq!( + oct_digit1.parse_peek(Partial::new(d)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(d), + ErrorKind::Slice + ))) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + //assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b))); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(d)), + Ok((Partial::new("é12"), "az")) + ); + assert_eq!( + space1::<_, InputError<_>>.parse_peek(Partial::new(e)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + use crate::stream::Offset; + #[test] + fn offset() { + let a = &b"abcd;"[..]; + let b = &b"1234;"[..]; + let c = &b"a123;"[..]; + let d = &b" \t;"[..]; + let e = &b" \t\r\n;"[..]; + let f = &b"123abcDEF;"[..]; + + match alpha1::<_, InputError<_>>.parse_peek(Partial::new(a)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&a) + i.len(), a.len()); + } + _ => panic!("wrong return type in offset test for alpha"), + } + match digit1::<_, InputError<_>>.parse_peek(Partial::new(b)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&b) + i.len(), b.len()); + } + _ => panic!("wrong return type in offset test for digit"), + } + match alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&c) + i.len(), c.len()); + } + _ => panic!("wrong return type in offset test for alphanumeric"), + } + match space1::<_, InputError<_>>.parse_peek(Partial::new(d)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&d) + i.len(), d.len()); + } + _ => panic!("wrong return type in offset test for space"), + } + match multispace1::<_, InputError<_>>.parse_peek(Partial::new(e)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&e) + i.len(), e.len()); + } + _ => panic!("wrong return type in offset test for multispace"), + } + match hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(f)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&f) + i.len(), f.len()); + } + _ => panic!("wrong return type in offset test for hex_digit"), + } + match oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(f)) { + Ok((i, _)) => { + let i = i.into_inner(); + assert_eq!(i.offset_from(&f) + i.len(), f.len()); + } + _ => panic!("wrong return type in offset test for oct_digit"), + } + } + + #[test] + fn is_not_line_ending_bytes() { + let a: &[u8] = b"ab12cd\nefgh"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(a)), + Ok((Partial::new(&b"\nefgh"[..]), &b"ab12cd"[..])) + ); + + let b: &[u8] = b"ab12cd\nefgh\nijkl"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(b)), + Ok((Partial::new(&b"\nefgh\nijkl"[..]), &b"ab12cd"[..])) + ); + + let c: &[u8] = b"ab12cd\r\nefgh\nijkl"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(c)), + Ok((Partial::new(&b"\r\nefgh\nijkl"[..]), &b"ab12cd"[..])) + ); + + let d: &[u8] = b"ab12cd"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(d)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn is_not_line_ending_str() { + let f = "βèƒôřè\rÂßÇáƒƭèř"; + assert_eq!( + not_line_ending.parse_peek(Partial::new(f)), + Err(ErrMode::Backtrack(InputError::new( + Partial::new(&f[12..]), + ErrorKind::Tag + ))) + ); + + let g2: &str = "ab12cd"; + assert_eq!( + not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(g2)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn hex_digit_test() { + let i = &b"0123456789abcdefABCDEF;"[..]; + assert_parse!( + hex_digit1.parse_peek(Partial::new(i)), + Ok((Partial::new(&b";"[..]), &i[..i.len() - 1])) + ); + + let i = &b"g"[..]; + assert_parse!( + hex_digit1.parse_peek(Partial::new(i)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(i), + ErrorKind::Slice + ))) + ); + + let i = &b"G"[..]; + assert_parse!( + hex_digit1.parse_peek(Partial::new(i)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(i), + ErrorKind::Slice + ))) + ); + + assert!(AsChar::is_hex_digit(b'0')); + assert!(AsChar::is_hex_digit(b'9')); + assert!(AsChar::is_hex_digit(b'a')); + assert!(AsChar::is_hex_digit(b'f')); + assert!(AsChar::is_hex_digit(b'A')); + assert!(AsChar::is_hex_digit(b'F')); + assert!(!AsChar::is_hex_digit(b'g')); + assert!(!AsChar::is_hex_digit(b'G')); + assert!(!AsChar::is_hex_digit(b'/')); + assert!(!AsChar::is_hex_digit(b':')); + assert!(!AsChar::is_hex_digit(b'@')); + assert!(!AsChar::is_hex_digit(b'\x60')); + } + + #[test] + fn oct_digit_test() { + let i = &b"01234567;"[..]; + assert_parse!( + oct_digit1.parse_peek(Partial::new(i)), + Ok((Partial::new(&b";"[..]), &i[..i.len() - 1])) + ); + + let i = &b"8"[..]; + assert_parse!( + oct_digit1.parse_peek(Partial::new(i)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(i), + ErrorKind::Slice + ))) + ); + + assert!(AsChar::is_oct_digit(b'0')); + assert!(AsChar::is_oct_digit(b'7')); + assert!(!AsChar::is_oct_digit(b'8')); + assert!(!AsChar::is_oct_digit(b'9')); + assert!(!AsChar::is_oct_digit(b'a')); + assert!(!AsChar::is_oct_digit(b'A')); + assert!(!AsChar::is_oct_digit(b'/')); + assert!(!AsChar::is_oct_digit(b':')); + assert!(!AsChar::is_oct_digit(b'@')); + assert!(!AsChar::is_oct_digit(b'\x60')); + } + + #[test] + fn full_line_windows() { + #[allow(clippy::type_complexity)] + fn take_full_line(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8])> { + (not_line_ending, line_ending).parse_peek(i) + } + let input = b"abc\r\n"; + let output = take_full_line(Partial::new(input)); + assert_eq!( + output, + Ok((Partial::new(&b""[..]), (&b"abc"[..], &b"\r\n"[..]))) + ); + } + + #[test] + fn full_line_unix() { + #[allow(clippy::type_complexity)] + fn take_full_line(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8])> { + (not_line_ending, line_ending).parse_peek(i) + } + let input = b"abc\n"; + let output = take_full_line(Partial::new(input)); + assert_eq!( + output, + Ok((Partial::new(&b""[..]), (&b"abc"[..], &b"\n"[..]))) + ); + } + + #[test] + fn check_windows_lineending() { + let input = b"\r\n"; + let output = line_ending.parse_peek(Partial::new(&input[..])); + assert_parse!(output, Ok((Partial::new(&b""[..]), &b"\r\n"[..]))); + } + + #[test] + fn check_unix_lineending() { + let input = b"\n"; + let output = line_ending.parse_peek(Partial::new(&input[..])); + assert_parse!(output, Ok((Partial::new(&b""[..]), &b"\n"[..]))); + } + + #[test] + fn cr_lf() { + assert_parse!( + crlf.parse_peek(Partial::new(&b"\r\na"[..])), + Ok((Partial::new(&b"a"[..]), &b"\r\n"[..])) + ); + assert_parse!( + crlf.parse_peek(Partial::new(&b"\r"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_parse!( + crlf.parse_peek(Partial::new(&b"\ra"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"\ra"[..]), + ErrorKind::Tag + ))) + ); + + assert_parse!( + crlf.parse_peek(Partial::new("\r\na")), + Ok((Partial::new("a"), "\r\n")) + ); + assert_parse!( + crlf.parse_peek(Partial::new("\r")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_parse!( + crlf.parse_peek(Partial::new("\ra")), + Err(ErrMode::Backtrack(error_position!( + &Partial::new("\ra"), + ErrorKind::Tag + ))) + ); + } + + #[test] + fn end_of_line() { + assert_parse!( + line_ending.parse_peek(Partial::new(&b"\na"[..])), + Ok((Partial::new(&b"a"[..]), &b"\n"[..])) + ); + assert_parse!( + line_ending.parse_peek(Partial::new(&b"\r\na"[..])), + Ok((Partial::new(&b"a"[..]), &b"\r\n"[..])) + ); + assert_parse!( + line_ending.parse_peek(Partial::new(&b"\r"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_parse!( + line_ending.parse_peek(Partial::new(&b"\ra"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"\ra"[..]), + ErrorKind::Tag + ))) + ); + + assert_parse!( + line_ending.parse_peek(Partial::new("\na")), + Ok((Partial::new("a"), "\n")) + ); + assert_parse!( + line_ending.parse_peek(Partial::new("\r\na")), + Ok((Partial::new("a"), "\r\n")) + ); + assert_parse!( + line_ending.parse_peek(Partial::new("\r")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_parse!( + line_ending.parse_peek(Partial::new("\ra")), + Err(ErrMode::Backtrack(error_position!( + &Partial::new("\ra"), + ErrorKind::Tag + ))) + ); + } + + 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> { + hex_uint.parse_peek(input) + } + + assert_parse!( + hex_u32(Partial::new(&b";"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b";"[..]), + ErrorKind::Slice + ))) + ); + assert_parse!( + hex_u32(Partial::new(&b"ff;"[..])), + Ok((Partial::new(&b";"[..]), 255)) + ); + assert_parse!( + hex_u32(Partial::new(&b"1be2;"[..])), + Ok((Partial::new(&b";"[..]), 7138)) + ); + assert_parse!( + hex_u32(Partial::new(&b"c5a31be2;"[..])), + Ok((Partial::new(&b";"[..]), 3_315_801_058)) + ); + assert_parse!( + hex_u32(Partial::new(&b"C5A31be2;"[..])), + Ok((Partial::new(&b";"[..]), 3_315_801_058)) + ); + assert_parse!( + hex_u32(Partial::new(&b"00c5a31be2;"[..])), // overflow + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"00c5a31be2;"[..]), + ErrorKind::Verify + ))) + ); + assert_parse!( + hex_u32(Partial::new(&b"c5a31be201;"[..])), // overflow + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"c5a31be201;"[..]), + ErrorKind::Verify + ))) + ); + assert_parse!( + hex_u32(Partial::new(&b"ffffffff;"[..])), + Ok((Partial::new(&b";"[..]), 4_294_967_295)) + ); + assert_parse!( + hex_u32(Partial::new(&b"ffffffffffffffff;"[..])), // overflow + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"ffffffffffffffff;"[..]), + ErrorKind::Verify + ))) + ); + assert_parse!( + hex_u32(Partial::new(&b"ffffffffffffffff"[..])), // overflow + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"ffffffffffffffff"[..]), + ErrorKind::Verify + ))) + ); + assert_parse!( + hex_u32(Partial::new(&b"0x1be2;"[..])), + Ok((Partial::new(&b"x1be2;"[..]), 0)) + ); + assert_parse!( + hex_u32(Partial::new(&b"12af"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } +} diff --git a/vendor/winnow/src/binary/bits/mod.rs b/vendor/winnow/src/binary/bits/mod.rs new file mode 100644 index 0000000..b11ba43 --- /dev/null +++ b/vendor/winnow/src/binary/bits/mod.rs @@ -0,0 +1,365 @@ +//! Bit level parsers +//! + +#[cfg(test)] +mod tests; + +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::trace::trace; +use crate::{unpeek, IResult, PResult, Parser}; + +/// Number of bits in a byte +const BYTE: usize = u8::BITS as usize; + +/// Converts a byte-level input to a bit-level input +/// +/// See [`bytes`] to convert it back. +/// +/// # Example +/// ``` +/// use winnow::prelude::*; +/// use winnow::Bytes; +/// use winnow::binary::bits::{bits, take}; +/// use winnow::error::InputError; +/// +/// type Stream<'i> = &'i Bytes; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Bytes::new(b) +/// } +/// +/// fn parse(input: Stream<'_>) -> IResult<Stream<'_>, (u8, u8)> { +/// bits::<_, _, InputError<(_, usize)>, _, _>((take(4usize), take(8usize))).parse_peek(input) +/// } +/// +/// let input = stream(&[0x12, 0x34, 0xff, 0xff]); +/// +/// let output = parse(input).expect("We take 1.5 bytes and the input is longer than 2 bytes"); +/// +/// // The first byte is consumed, the second byte is partially consumed and dropped. +/// let remaining = output.0; +/// assert_eq!(remaining, stream(&[0xff, 0xff])); +/// +/// let parsed = output.1; +/// 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> +where + E1: ParserError<(I, usize)> + ErrorConvert<E2>, + E2: ParserError<I>, + I: Stream + Clone, + P: Parser<(I, usize), O, E1>, +{ + 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()), + } + }), + ) +} + +/// Convert a [`bits`] stream back into a byte stream +/// +/// **Warning:** A partial byte remaining in the input will be ignored and the given parser will +/// start parsing at the next full byte. +/// +/// ``` +/// use winnow::prelude::*; +/// use winnow::Bytes; +/// use winnow::binary::bits::{bits, bytes, take}; +/// use winnow::combinator::rest; +/// use winnow::error::InputError; +/// +/// type Stream<'i> = &'i Bytes; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Bytes::new(b) +/// } +/// +/// fn parse(input: Stream<'_>) -> IResult<Stream<'_>, (u8, u8, &[u8])> { +/// bits::<_, _, InputError<(_, usize)>, _, _>(( +/// take(4usize), +/// take(8usize), +/// bytes::<_, _, InputError<_>, _, _>(rest) +/// )).parse_peek(input) +/// } +/// +/// let input = stream(&[0x12, 0x34, 0xff, 0xff]); +/// +/// 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> +where + E1: ParserError<I> + ErrorConvert<E2>, + E2: ParserError<(I, usize)>, + I: Stream<Token = u8> + Clone, + P: Parser<I, O, E1>, +{ + 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()), + } + }), + ) +} + +/// Parse taking `count` bits +/// +/// # Example +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::Bytes; +/// # use winnow::error::{InputError, ErrorKind}; +/// use winnow::binary::bits::take; +/// +/// type Stream<'i> = &'i Bytes; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Bytes::new(b) +/// } +/// +/// fn parser(input: (Stream<'_>, usize), count: usize)-> IResult<(Stream<'_>, usize), u8> { +/// take(count).parse_peek(input) +/// } +/// +/// // Consumes 0 bits, returns 0 +/// assert_eq!(parser((stream(&[0b00010010]), 0), 0), Ok(((stream(&[0b00010010]), 0), 0))); +/// +/// // Consumes 4 bits, returns their values and increase offset to 4 +/// assert_eq!(parser((stream(&[0b00010010]), 0), 4), Ok(((stream(&[0b00010010]), 4), 0b00000001))); +/// +/// // Consumes 4 bits, offset is 4, returns their values and increase offset to 0 of next byte +/// assert_eq!(parser((stream(&[0b00010010]), 4), 4), Ok(((stream(&[]), 0), 0b00000010))); +/// +/// // Tries to consume 12 bits but only 8 are available +/// 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> +where + I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone, + C: ToUsize, + O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>, +{ + 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) + } + }), + ) +} + +fn take_<I, O, E: ParserError<(I, usize)>, const PARTIAL: bool>( + (input, bit_offset): (I, usize), + count: usize, +) -> IResult<(I, usize), O, E> +where + I: StreamIsPartial, + I: Stream<Token = u8> + AsBytes + Clone, + O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>, +{ + if count == 0 { + Ok(((input, bit_offset), 0u8.into())) + } else { + if input.eof_offset() * BYTE < count + bit_offset { + if PARTIAL && input.is_partial() { + Err(ErrMode::Incomplete(Needed::new(count))) + } else { + Err(ErrMode::from_error_kind( + &(input, bit_offset), + ErrorKind::Eof, + )) + } + } else { + let cnt = (count + bit_offset).div(BYTE); + let mut acc: O = 0_u8.into(); + let mut offset: usize = bit_offset; + let mut remaining: usize = count; + let mut end_offset: usize = 0; + + for byte in input.as_bytes().iter().copied().take(cnt + 1) { + if remaining == 0 { + break; + } + let val: O = if offset == 0 { + byte.into() + } else { + (byte << offset >> offset).into() + }; + + if remaining < BYTE - offset { + acc += val >> (BYTE - offset - remaining); + end_offset = remaining + offset; + break; + } else { + acc += val << (remaining - (BYTE - offset)); + remaining -= BYTE - offset; + offset = 0; + } + } + let (input, _) = input.peek_slice(cnt); + Ok(((input, end_offset), acc)) + } + } +} + +/// Parse taking `count` bits and comparing them to `pattern` +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::Bytes; +/// # use winnow::error::{InputError, ErrorKind}; +/// use winnow::binary::bits::tag; +/// +/// type Stream<'i> = &'i Bytes; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Bytes::new(b) +/// } +/// +/// /// 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) +/// } +/// +/// // The lowest 4 bits of 0b00001111 match the lowest 4 bits of 0b11111111. +/// assert_eq!( +/// parser(0b0000_1111, 4, (stream(&[0b1111_1111]), 0)), +/// Ok(((stream(&[0b1111_1111]), 4), 0b0000_1111)) +/// ); +/// +/// // The lowest bit of 0b00001111 matches the lowest bit of 0b11111111 (both are 1). +/// assert_eq!( +/// parser(0b00000001, 1, (stream(&[0b11111111]), 0)), +/// Ok(((stream(&[0b11111111]), 1), 0b00000001)) +/// ); +/// +/// // The lowest 2 bits of 0b11111111 and 0b00000001 are different. +/// assert_eq!( +/// parser(0b000000_01, 2, (stream(&[0b111111_11]), 0)), +/// Err(winnow::error::ErrMode::Backtrack(InputError::new( +/// (stream(&[0b11111111]), 0), +/// ErrorKind::Tag +/// ))) +/// ); +/// +/// // The lowest 8 bits of 0b11111111 and 0b11111110 are different. +/// assert_eq!( +/// parser(0b11111110, 8, (stream(&[0b11111111]), 0)), +/// Err(winnow::error::ErrMode::Backtrack(InputError::new( +/// (stream(&[0b11111111]), 0), +/// ErrorKind::Tag +/// ))) +/// ); +/// ``` +#[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> +where + I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone, + C: ToUsize, + O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O> + PartialEq, +{ + let count = count.to_usize(); + trace("tag", move |input: &mut (I, 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, + ErrorKind::Tag, + ))) + } + }) + }) +} + +/// Parses one specific bit as a bool. +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::Bytes; +/// # use winnow::error::{InputError, ErrorKind}; +/// use winnow::binary::bits::bool; +/// +/// type Stream<'i> = &'i Bytes; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Bytes::new(b) +/// } +/// +/// fn parse(input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), bool> { +/// bool.parse_peek(input) +/// } +/// +/// assert_eq!(parse((stream(&[0b10000000]), 0)), Ok(((stream(&[0b10000000]), 1), true))); +/// 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> +where + I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone, +{ + trace("bool", |input: &mut (I, usize)| { + let bit: u32 = take(1usize).parse_next(input)?; + Ok(bit != 0) + }) + .parse_next(input) +} diff --git a/vendor/winnow/src/binary/bits/tests.rs b/vendor/winnow/src/binary/bits/tests.rs new file mode 100644 index 0000000..41207c6 --- /dev/null +++ b/vendor/winnow/src/binary/bits/tests.rs @@ -0,0 +1,191 @@ +use super::*; +use crate::error::InputError; +use crate::Partial; + +#[test] +/// Take the `bits` function and assert that remaining bytes are correctly returned, if the +/// previous bytes are fully consumed +fn test_complete_byte_consumption_bits() { + let input = &[0x12, 0x34, 0x56, 0x78][..]; + + // Take 3 bit slices with sizes [4, 8, 4]. + let result: IResult<&[u8], (u8, u8, u8)> = + bits::<_, _, InputError<(&[u8], usize)>, _, _>((take(4usize), take(8usize), take(4usize))) + .parse_peek(input); + + let output = result.expect("We take 2 bytes and the input is longer than 2 bytes"); + + let remaining = output.0; + assert_eq!(remaining, [0x56, 0x78]); + + let parsed = output.1; + assert_eq!(parsed.0, 0x01); + assert_eq!(parsed.1, 0x23); + assert_eq!(parsed.2, 0x04); +} + +#[test] +/// Take the `bits` function and assert that remaining bytes are correctly returned, if the +/// previous bytes are NOT fully consumed. Partially consumed bytes are supposed to be dropped. +/// I.e. if we consume 1.5 bytes of 4 bytes, 2 bytes will be returned, bits 13-16 will be +/// dropped. +fn test_partial_byte_consumption_bits() { + let input = &[0x12, 0x34, 0x56, 0x78][..]; + + // Take bit slices with sizes [4, 8]. + let result: IResult<&[u8], (u8, u8)> = + bits::<_, _, InputError<(&[u8], usize)>, _, _>((take(4usize), take(8usize))) + .parse_peek(input); + + let output = result.expect("We take 1.5 bytes and the input is longer than 2 bytes"); + + let remaining = output.0; + assert_eq!(remaining, [0x56, 0x78]); + + let parsed = output.1; + assert_eq!(parsed.0, 0x01); + assert_eq!(parsed.1, 0x23); +} + +#[test] +#[cfg(feature = "std")] +/// Ensure that in Incomplete error is thrown, if too few bytes are passed for a given parser. +fn test_incomplete_bits() { + let input = Partial::new(&[0x12][..]); + + // Take bit slices with sizes [4, 8]. + let result: IResult<_, (u8, u8)> = + bits::<_, _, InputError<(_, usize)>, _, _>((take(4usize), take(8usize))).parse_peek(input); + + assert!(result.is_err()); + let error = result.err().unwrap(); + assert_eq!("Parsing requires 2 bytes/chars", error.to_string()); +} + +#[test] +fn test_take_complete_0() { + let input = &[0b00010010][..]; + let count = 0usize; + assert_eq!(count, 0usize); + let offset = 0usize; + + let result: crate::IResult<(&[u8], usize), usize> = take(count).parse_peek((input, offset)); + + assert_eq!(result, Ok(((input, offset), 0))); +} + +#[test] +fn test_take_complete_eof() { + let input = &[0b00010010][..]; + + let result: crate::IResult<(&[u8], usize), usize> = take(1usize).parse_peek((input, 8)); + + assert_eq!( + result, + Err(crate::error::ErrMode::Backtrack(InputError::new( + (input, 8), + ErrorKind::Eof + ))) + ); +} + +#[test] +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)); + + assert_eq!( + result, + Ok((([0b11111111].as_ref(), 4), 0b1000110100111111111111)) + ); +} + +#[test] +fn test_take_partial_0() { + let input = Partial::new(&[][..]); + let count = 0usize; + assert_eq!(count, 0usize); + let offset = 0usize; + + let result: crate::IResult<(_, usize), usize> = take(count).parse_peek((input, offset)); + + assert_eq!(result, Ok(((input, offset), 0))); +} + +#[test] +fn test_tag_partial_ok() { + let input = Partial::new(&[0b00011111][..]); + let offset = 0usize; + let bits_to_take = 4usize; + let value_to_tag = 0b0001; + + let result: crate::IResult<(_, usize), usize> = + tag(value_to_tag, bits_to_take).parse_peek((input, offset)); + + assert_eq!(result, Ok(((input, bits_to_take), value_to_tag))); +} + +#[test] +fn test_tag_partial_err() { + let input = Partial::new(&[0b00011111][..]); + let offset = 0usize; + let bits_to_take = 4usize; + let value_to_tag = 0b1111; + + let result: crate::IResult<(_, usize), usize> = + tag(value_to_tag, bits_to_take).parse_peek((input, offset)); + + assert_eq!( + result, + Err(crate::error::ErrMode::Backtrack(InputError::new( + (input, offset), + ErrorKind::Tag + ))) + ); +} + +#[test] +fn test_bool_0_complete() { + let input = [0b10000000].as_ref(); + + let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 0)); + + assert_eq!(result, Ok(((input, 1), true))); +} + +#[test] +fn test_bool_eof_complete() { + let input = [0b10000000].as_ref(); + + let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 8)); + + assert_eq!( + result, + Err(crate::error::ErrMode::Backtrack(InputError::new( + (input, 8), + ErrorKind::Eof + ))) + ); +} + +#[test] +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)); + + assert_eq!(result, Ok(((input, 1), true))); +} + +#[test] +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)); + + assert_eq!( + result, + Err(crate::error::ErrMode::Incomplete(Needed::new(1))) + ); +} diff --git a/vendor/winnow/src/binary/mod.rs b/vendor/winnow/src/binary/mod.rs new file mode 100644 index 0000000..8b2ee74 --- /dev/null +++ b/vendor/winnow/src/binary/mod.rs @@ -0,0 +1,2550 @@ +//! Parsers recognizing numbers + +#![allow(clippy::match_same_arms)] + +pub mod bits; + +#[cfg(test)] +mod tests; + +use crate::combinator::repeat; +use crate::error::ErrMode; +use crate::error::ErrorKind; +use crate::error::Needed; +use crate::error::ParserError; +use crate::lib::std::ops::{Add, Shl}; +use crate::stream::Accumulate; +use crate::stream::{AsBytes, Stream, StreamIsPartial}; +use crate::stream::{ToUsize, UpdateSlice}; +use crate::token::take; +use crate::trace::trace; +use crate::PResult; +use crate::Parser; + +/// Configurable endianness +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Endianness { + /// Big endian + Big, + /// Little endian + Little, + /// Will match the host's endianness + Native, +} + +/// Recognizes an unsigned 1 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_u8; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u8> { +/// be_u8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); +/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_u8; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> { +/// be_u8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + u8(input) +} + +/// Recognizes a big endian unsigned 2 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_u16; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u16> { +/// be_u16.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_u16; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> { +/// be_u16.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_u16", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_u24; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u32> { +/// be_u24.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_u24; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { +/// be_u24.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x000102))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_u23", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_u32; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u32> { +/// be_u32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_u32; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { +/// be_u32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_u32", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_u64; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u64> { +/// be_u64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_u64; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> { +/// be_u64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001020304050607))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_u64", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_u128; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u128> { +/// be_u128.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_u128; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u128> { +/// be_u128.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203040506070809101112131415))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_u128", move |input: &mut I| be_uint(input, 16)).parse_next(input) +} + +#[inline] +fn be_uint<I, Uint, E: ParserError<I>>(input: &mut I, bound: usize) -> PResult<Uint, E> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, + Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>, +{ + 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) +} + +#[inline] +fn to_be_uint<Uint>(number: &[u8]) -> Uint +where + Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>, +{ + let mut res = Uint::default(); + for byte in number.iter().copied() { + res = (res << 8) + byte.into(); + } + + res +} + +/// Recognizes a signed 1 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_i8; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i8> { +/// be_i8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); +/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_i8; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i8> { +/// be_i8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + i8(input) +} + +/// Recognizes a big endian signed 2 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_i16; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i16> { +/// be_i16.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_i16; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> { +/// be_i16.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_i16", move |input: &mut I| { + be_uint::<_, u16, _>(input, 2).map(|n| n as i16) + }) + .parse_next(input) +} + +/// Recognizes a big endian signed 3 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_i24; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i32> { +/// be_i24.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_i24; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> { +/// be_i24.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x000102))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_i24", move |input: &mut I| { + 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 { + (n | 0xff_00_00_00) as i32 + } else { + n as i32 + }; + n + }) + }) + .parse_next(input) +} + +/// Recognizes a big endian signed 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_i32; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i32> { +/// be_i32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_i32; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> { +/// be_i32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_i32", move |input: &mut I| { + be_uint::<_, u32, _>(input, 4).map(|n| n as i32) + }) + .parse_next(input) +} + +/// Recognizes a big endian signed 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_i64; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i64> { +/// be_i64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_i64; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> { +/// be_i64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001020304050607))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_i64", move |input: &mut I| { + be_uint::<_, u64, _>(input, 8).map(|n| n as i64) + }) + .parse_next(input) +} + +/// Recognizes a big endian signed 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_i128; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i128> { +/// be_i128.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_i128; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i128> { +/// be_i128.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203040506070809101112131415))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_i128", move |input: &mut I| { + be_uint::<_, u128, _>(input, 16).map(|n| n as i128) + }) + .parse_next(input) +} + +/// Recognizes an unsigned 1 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_u8; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u8> { +/// le_u8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); +/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_u8; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> { +/// le_u8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + u8(input) +} + +/// Recognizes a little endian unsigned 2 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_u16; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u16> { +/// le_u16.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_u16; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> { +/// le_u16::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_u16", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_u24; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u32> { +/// le_u24.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_u24; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { +/// le_u24::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_u24", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_u32; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u32> { +/// le_u32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_u32; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { +/// le_u32::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x03020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_u32", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_u64; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u64> { +/// le_u64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_u64; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> { +/// le_u64::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_u64", move |input: &mut I| 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_u128; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u128> { +/// le_u128.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_u128; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u128> { +/// le_u128::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x15141312111009080706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_u128", move |input: &mut I| le_uint(input, 16)).parse_next(input) +} + +#[inline] +fn le_uint<I, Uint, E: ParserError<I>>(input: &mut I, bound: usize) -> PResult<Uint, E> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, + Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>, +{ + take(bound) + .map(|n: <I as Stream>::Slice| to_le_uint(n.as_bytes())) + .parse_next(input) +} + +#[inline] +fn to_le_uint<Uint>(number: &[u8]) -> Uint +where + Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>, +{ + let mut res = Uint::default(); + for (index, byte) in number.iter_offsets() { + res = res + (Uint::from(byte) << (8 * index as u8)); + } + + res +} + +/// Recognizes a signed 1 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_i8; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i8> { +/// le_i8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); +/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_i8; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i8> { +/// le_i8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + i8(input) +} + +/// Recognizes a little endian signed 2 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_i16; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i16> { +/// le_i16.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_i16; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> { +/// le_i16::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_i16", move |input: &mut I| { + le_uint::<_, u16, _>(input, 2).map(|n| n as i16) + }) + .parse_next(input) +} + +/// Recognizes a little endian signed 3 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_i24; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i32> { +/// le_i24.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_i24; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> { +/// le_i24::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_i24", move |input: &mut I| { + 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 { + (n | 0xff_00_00_00) as i32 + } else { + n as i32 + }; + n + }) + }) + .parse_next(input) +} + +/// Recognizes a little endian signed 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_i32; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i32> { +/// le_i32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_i32; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> { +/// le_i32::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x03020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_i32", move |input: &mut I| { + le_uint::<_, u32, _>(input, 4).map(|n| n as i32) + }) + .parse_next(input) +} + +/// Recognizes a little endian signed 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_i64; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i64> { +/// le_i64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_i64; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> { +/// le_i64::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_i64", move |input: &mut I| { + le_uint::<_, u64, _>(input, 8).map(|n| n as i64) + }) + .parse_next(input) +} + +/// Recognizes a little endian signed 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_i128; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i128> { +/// le_i128.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); +/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_i128; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i128> { +/// le_i128::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x15141312111009080706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_i128", move |input: &mut I| { + le_uint::<_, u128, _>(input, 16).map(|n| n as i128) + }) + .parse_next(input) +} + +/// Recognizes an unsigned 1 byte integer +/// +/// **Note:** that endianness does not apply to 1 byte numbers. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::u8; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], u8> { +/// u8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); +/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::u8; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> { +/// u8::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"\x03abcefg"[..]), 0x00))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + trace("u8", move |input: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + u8_::<_, _, true>(input) + } else { + u8_::<_, _, false>(input) + } + }) + .parse_next(input) +} + +fn u8_<I, E: ParserError<I>, const PARTIAL: bool>(input: &mut I) -> PResult<u8, E> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + 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)) + } + }) +} + +/// Recognizes an unsigned 2 bytes integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u16 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u16 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::u16; +/// +/// let be_u16 = |s| { +/// u16(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); +/// assert_eq!(be_u16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_u16 = |s| { +/// u16(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); +/// assert_eq!(le_u16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::u16; +/// +/// let be_u16 = |s| { +/// u16::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0003))); +/// assert_eq!(be_u16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); +/// +/// let le_u16 = |s| { +/// u16::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0300))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_u16, + Endianness::Little => le_u16, + #[cfg(target_endian = "big")] + Endianness::Native => be_u16, + #[cfg(target_endian = "little")] + Endianness::Native => le_u16, + } + }(input) +} + +/// Recognizes an unsigned 3 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u24 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u24 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::u24; +/// +/// let be_u24 = |s| { +/// u24(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); +/// assert_eq!(be_u24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_u24 = |s| { +/// u24(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); +/// assert_eq!(le_u24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::u24; +/// +/// let be_u24 = |s| { +/// u24::<_,InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x000305))); +/// assert_eq!(be_u24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2)))); +/// +/// let le_u24 = |s| { +/// u24::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x050300))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_u24, + Endianness::Little => le_u24, + #[cfg(target_endian = "big")] + Endianness::Native => be_u24, + #[cfg(target_endian = "little")] + Endianness::Native => le_u24, + } + }(input) +} + +/// Recognizes an unsigned 4 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u32 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u32 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::u32; +/// +/// let be_u32 = |s| { +/// u32(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); +/// assert_eq!(be_u32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_u32 = |s| { +/// u32(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); +/// assert_eq!(le_u32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::u32; +/// +/// let be_u32 = |s| { +/// u32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00030507))); +/// assert_eq!(be_u32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3)))); +/// +/// let le_u32 = |s| { +/// u32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07050300))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_u32, + Endianness::Little => le_u32, + #[cfg(target_endian = "big")] + Endianness::Native => be_u32, + #[cfg(target_endian = "little")] + Endianness::Native => le_u32, + } + }(input) +} + +/// Recognizes an unsigned 8 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u64 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u64 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::u64; +/// +/// let be_u64 = |s| { +/// u64(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); +/// assert_eq!(be_u64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_u64 = |s| { +/// u64(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); +/// assert_eq!(le_u64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::u64; +/// +/// let be_u64 = |s| { +/// u64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0001020304050607))); +/// assert_eq!(be_u64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7)))); +/// +/// let le_u64 = |s| { +/// u64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_u64, + Endianness::Little => le_u64, + #[cfg(target_endian = "big")] + Endianness::Native => be_u64, + #[cfg(target_endian = "little")] + Endianness::Native => le_u64, + } + }(input) +} + +/// Recognizes an unsigned 16 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u128 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u128 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::u128; +/// +/// let be_u128 = |s| { +/// u128(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); +/// assert_eq!(be_u128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_u128 = |s| { +/// u128(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); +/// assert_eq!(le_u128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::u128; +/// +/// let be_u128 = |s| { +/// u128::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_u128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00010203040506070001020304050607))); +/// assert_eq!(be_u128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15)))); +/// +/// let le_u128 = |s| { +/// u128::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_u128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07060504030201000706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_u128, + Endianness::Little => le_u128, + #[cfg(target_endian = "big")] + Endianness::Native => be_u128, + #[cfg(target_endian = "little")] + Endianness::Native => le_u128, + } + }(input) +} + +/// Recognizes a signed 1 byte integer +/// +/// **Note:** that endianness does not apply to 1 byte numbers. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::i8; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], i8> { +/// i8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); +/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::i8; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i8> { +/// i8.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"\x03abcefg"[..]), 0x00))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, +{ + trace("i8", move |input: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + u8_::<_, _, true>(input) + } else { + u8_::<_, _, false>(input) + } + .map(|n| n as i8) + }) + .parse_next(input) +} + +/// Recognizes a signed 2 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i16 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i16 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::i16; +/// +/// let be_i16 = |s| { +/// i16(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); +/// assert_eq!(be_i16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_i16 = |s| { +/// i16(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); +/// assert_eq!(le_i16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::i16; +/// +/// let be_i16 = |s| { +/// i16::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0003))); +/// assert_eq!(be_i16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); +/// +/// let le_i16 = |s| { +/// i16::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0300))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_i16, + Endianness::Little => le_i16, + #[cfg(target_endian = "big")] + Endianness::Native => be_i16, + #[cfg(target_endian = "little")] + Endianness::Native => le_i16, + } + }(input) +} + +/// Recognizes a signed 3 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i24 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i24 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::i24; +/// +/// let be_i24 = |s| { +/// i24(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); +/// assert_eq!(be_i24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_i24 = |s| { +/// i24(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); +/// assert_eq!(le_i24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::i24; +/// +/// let be_i24 = |s| { +/// i24::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x000305))); +/// assert_eq!(be_i24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2)))); +/// +/// let le_i24 = |s| { +/// i24::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x050300))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_i24, + Endianness::Little => le_i24, + #[cfg(target_endian = "big")] + Endianness::Native => be_i24, + #[cfg(target_endian = "little")] + Endianness::Native => le_i24, + } + }(input) +} + +/// Recognizes a signed 4 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i32 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i32 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::i32; +/// +/// let be_i32 = |s| { +/// i32(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); +/// assert_eq!(be_i32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_i32 = |s| { +/// i32(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); +/// assert_eq!(le_i32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::i32; +/// +/// let be_i32 = |s| { +/// i32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00030507))); +/// assert_eq!(be_i32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3)))); +/// +/// let le_i32 = |s| { +/// i32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07050300))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_i32, + Endianness::Little => le_i32, + #[cfg(target_endian = "big")] + Endianness::Native => be_i32, + #[cfg(target_endian = "little")] + Endianness::Native => le_i32, + } + }(input) +} + +/// Recognizes a signed 8 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i64 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i64 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::i64; +/// +/// let be_i64 = |s| { +/// i64(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); +/// assert_eq!(be_i64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_i64 = |s| { +/// i64(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); +/// assert_eq!(le_i64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::i64; +/// +/// let be_i64 = |s| { +/// i64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0001020304050607))); +/// assert_eq!(be_i64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7)))); +/// +/// let le_i64 = |s| { +/// i64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_i64, + Endianness::Little => le_i64, + #[cfg(target_endian = "big")] + Endianness::Native => be_i64, + #[cfg(target_endian = "little")] + Endianness::Native => le_i64, + } + }(input) +} + +/// Recognizes a signed 16 byte integer +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i128 integer, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i128 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::i128; +/// +/// let be_i128 = |s| { +/// i128(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); +/// assert_eq!(be_i128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// +/// let le_i128 = |s| { +/// i128(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); +/// assert_eq!(le_i128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::i128; +/// +/// let be_i128 = |s| { +/// i128::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_i128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00010203040506070001020304050607))); +/// assert_eq!(be_i128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15)))); +/// +/// let le_i128 = |s| { +/// i128::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_i128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07060504030201000706050403020100))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_i128, + Endianness::Little => le_i128, + #[cfg(target_endian = "big")] + Endianness::Native => be_i128, + #[cfg(target_endian = "little")] + Endianness::Native => le_i128, + } + }(input) +} + +/// Recognizes a big endian 4 bytes floating point number. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_f32; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], f32> { +/// be_f32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&[0x41, 0x48, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_f32; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f32> { +/// be_f32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&[0x40, 0x29, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 2.640625))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_f32", move |input: &mut I| { + be_uint::<_, u32, _>(input, 4).map(f32::from_bits) + }) + .parse_next(input) +} + +/// Recognizes a big endian 8 bytes floating point number. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::be_f64; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], f64> { +/// be_f64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::be_f64; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f64> { +/// be_f64::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_f64", move |input: &mut I| { + be_uint::<_, u64, _>(input, 8).map(f64::from_bits) + }) + .parse_next(input) +} + +/// Recognizes a little endian 4 bytes floating point number. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_f32; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], f32> { +/// le_f32.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&[0x00, 0x00, 0x48, 0x41][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_f32; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f32> { +/// le_f32::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&[0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 12.5))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("le_f32", move |input: &mut I| { + le_uint::<_, u32, _>(input, 4).map(f32::from_bits) + }) + .parse_next(input) +} + +/// Recognizes a little endian 8 bytes floating point number. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::le_f64; +/// +/// fn parser(s: &[u8]) -> IResult<&[u8], f64> { +/// le_f64.parse_peek(s) +/// } +/// +/// assert_eq!(parser(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::binary::le_f64; +/// +/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f64> { +/// le_f64::<_, InputError<_>>.parse_peek(s) +/// } +/// +/// assert_eq!(parser(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 3145728.0))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + trace("be_f64", move |input: &mut I| { + le_uint::<_, u64, _>(input, 8).map(f64::from_bits) + }) + .parse_next(input) +} + +/// Recognizes a 4 byte floating point number +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian f32 float, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian f32 float. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::f32; +/// +/// let be_f32 = |s| { +/// f32(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_f32(&[0x41, 0x48, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(be_f32(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// +/// let le_f32 = |s| { +/// f32(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_f32(&[0x00, 0x00, 0x48, 0x41][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(le_f32(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::f32; +/// +/// let be_f32 = |s| { +/// f32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_f32(Partial::new(&[0x41, 0x48, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5))); +/// assert_eq!(be_f32(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); +/// +/// let le_f32 = |s| { +/// f32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_f32(Partial::new(&[0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 12.5))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_f32, + Endianness::Little => le_f32, + #[cfg(target_endian = "big")] + Endianness::Native => be_f32, + #[cfg(target_endian = "little")] + Endianness::Native => le_f32, + } + }(input) +} + +/// Recognizes an 8 byte floating point number +/// +/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian f64 float, +/// otherwise if `winnow::binary::Endianness::Little` parse a little endian f64 float. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::binary::f64; +/// +/// let be_f64 = |s| { +/// f64(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_f64(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(be_f64(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// +/// let le_f64 = |s| { +/// f64(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..]), Ok((&b""[..], 12.5))); +/// assert_eq!(le_f64(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// # use winnow::Partial; +/// use winnow::binary::f64; +/// +/// let be_f64 = |s| { +/// f64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) +/// }; +/// +/// assert_eq!(be_f64(Partial::new(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5))); +/// assert_eq!(be_f64(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(5)))); +/// +/// let le_f64 = |s| { +/// f64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) +/// }; +/// +/// assert_eq!(le_f64(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..])), Ok((Partial::new(&b""[..]), 12.5))); +/// 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> +where + I: StreamIsPartial, + I: Stream<Token = u8>, + <I as Stream>::Slice: AsBytes, +{ + move |input: &mut I| { + match endian { + Endianness::Big => be_f64, + Endianness::Little => le_f64, + #[cfg(target_endian = "big")] + Endianness::Native => be_f64, + #[cfg(target_endian = "little")] + Endianness::Native => le_f64, + } + }(input) +} + +/// Gets a number from the parser and returns a +/// subslice of the input of that size. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed, stream::Partial}; +/// # use winnow::prelude::*; +/// use winnow::Bytes; +/// use winnow::binary::be_u16; +/// use winnow::binary::length_data; +/// use winnow::token::tag; +/// +/// type Stream<'i> = Partial<&'i Bytes>; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Partial::new(Bytes::new(b)) +/// } +/// +/// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, &[u8]> { +/// length_data(be_u16).parse_peek(s) +/// } +/// +/// 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_data<I, N, E, F>(mut 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>, +{ + trace("length_data", move |i: &mut I| { + let length = f.parse_next(i)?; + + crate::token::take(length).parse_next(i) + }) +} + +/// 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. +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed, stream::{Partial, StreamIsPartial}}; +/// # use winnow::prelude::*; +/// use winnow::Bytes; +/// use winnow::binary::be_u16; +/// use winnow::binary::length_value; +/// use winnow::token::tag; +/// +/// type Stream<'i> = Partial<&'i Bytes>; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Partial::new(Bytes::new(b)) +/// } +/// +/// fn complete_stream(b: &[u8]) -> Stream<'_> { +/// let mut p = Partial::new(Bytes::new(b)); +/// let _ = p.complete(); +/// p +/// } +/// +/// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, &[u8]> { +/// length_value(be_u16, "abc").parse_peek(s) +/// } +/// +/// assert_eq!(parser(stream(b"\x00\x03abcefg")), Ok((stream(&b"efg"[..]), &b"abc"[..]))); +/// 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_value<I, O, N, E, F, G>(mut f: F, mut 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>, +{ + trace("length_value", move |i: &mut I| { + let data = length_data(f.by_ref()).parse_next(i)?; + let mut data = I::update_slice(i.clone(), data); + let _ = data.complete(); + let o = g.by_ref().complete_err().parse_next(&mut data)?; + Ok(o) + }) +} + +/// Gets a number from the first parser, +/// then applies the second parser that many times. +/// +/// # Arguments +/// * `f` The parser to apply to obtain the count. +/// * `g` The parser to apply repeatedly. +/// +/// # Example +/// +/// ```rust +/// # #[cfg(feature = "std")] { +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::Bytes; +/// use winnow::binary::u8; +/// use winnow::binary::length_count; +/// use winnow::token::tag; +/// +/// type Stream<'i> = &'i Bytes; +/// +/// fn stream(b: &[u8]) -> Stream<'_> { +/// Bytes::new(b) +/// } +/// +/// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, Vec<&[u8]>> { +/// length_count(u8.map(|i| { +/// println!("got number: {}", i); +/// i +/// }), "abc").parse_peek(s) +/// } +/// +/// assert_eq!(parser(stream(b"\x02abcabcabc")), Ok((stream(b"abc"), vec![&b"abc"[..], &b"abc"[..]]))); +/// assert_eq!(parser(stream(b"\x03123123123")), Err(ErrMode::Backtrack(InputError::new(stream(b"123123123"), ErrorKind::Tag)))); +/// # } +/// ``` +pub fn length_count<I, O, C, N, E, F, G>(mut f: F, mut 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>, +{ + trace("length_count", move |i: &mut I| { + let n = f.parse_next(i)?; + let n = n.to_usize(); + repeat(n, g.by_ref()).parse_next(i) + }) +} diff --git a/vendor/winnow/src/binary/tests.rs b/vendor/winnow/src/binary/tests.rs new file mode 100644 index 0000000..5d92055 --- /dev/null +++ b/vendor/winnow/src/binary/tests.rs @@ -0,0 +1,1270 @@ +use super::*; +use crate::unpeek; +use crate::IResult; + +mod complete { + use super::*; + use crate::error::InputError; + + macro_rules! assert_parse( + ($left: expr, $right: expr) => { + let res: $crate::IResult<_, _, InputError<_>> = $left; + assert_eq!(res, $right); + }; + ); + + #[test] + fn i8_tests() { + assert_parse!(i8.parse_peek(&[0x00][..]), Ok((&b""[..], 0))); + assert_parse!(i8.parse_peek(&[0x7f][..]), Ok((&b""[..], 127))); + assert_parse!(i8.parse_peek(&[0xff][..]), Ok((&b""[..], -1))); + assert_parse!(i8.parse_peek(&[0x80][..]), Ok((&b""[..], -128))); + } + + #[test] + fn be_i8_tests() { + assert_parse!(be_i8.parse_peek(&[0x00][..]), Ok((&b""[..], 0))); + assert_parse!(be_i8.parse_peek(&[0x7f][..]), Ok((&b""[..], 127))); + assert_parse!(be_i8.parse_peek(&[0xff][..]), Ok((&b""[..], -1))); + assert_parse!(be_i8.parse_peek(&[0x80][..]), Ok((&b""[..], -128))); + } + + #[test] + fn be_i16_tests() { + assert_parse!(be_i16.parse_peek(&[0x00, 0x00][..]), Ok((&b""[..], 0))); + assert_parse!( + be_i16.parse_peek(&[0x7f, 0xff][..]), + Ok((&b""[..], 32_767_i16)) + ); + assert_parse!(be_i16.parse_peek(&[0xff, 0xff][..]), Ok((&b""[..], -1))); + assert_parse!( + be_i16.parse_peek(&[0x80, 0x00][..]), + Ok((&b""[..], -32_768_i16)) + ); + } + + #[test] + fn be_u24_tests() { + assert_parse!( + be_u24.parse_peek(&[0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0)) + ); + assert_parse!( + be_u24.parse_peek(&[0x00, 0xFF, 0xFF][..]), + Ok((&b""[..], 65_535_u32)) + ); + assert_parse!( + be_u24.parse_peek(&[0x12, 0x34, 0x56][..]), + Ok((&b""[..], 1_193_046_u32)) + ); + } + + #[test] + fn be_i24_tests() { + assert_parse!( + be_i24.parse_peek(&[0xFF, 0xFF, 0xFF][..]), + Ok((&b""[..], -1_i32)) + ); + assert_parse!( + be_i24.parse_peek(&[0xFF, 0x00, 0x00][..]), + Ok((&b""[..], -65_536_i32)) + ); + assert_parse!( + be_i24.parse_peek(&[0xED, 0xCB, 0xAA][..]), + Ok((&b""[..], -1_193_046_i32)) + ); + } + + #[test] + fn be_i32_tests() { + assert_parse!( + be_i32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0)) + ); + assert_parse!( + be_i32.parse_peek(&[0x7f, 0xff, 0xff, 0xff][..]), + Ok((&b""[..], 2_147_483_647_i32)) + ); + assert_parse!( + be_i32.parse_peek(&[0xff, 0xff, 0xff, 0xff][..]), + Ok((&b""[..], -1)) + ); + assert_parse!( + be_i32.parse_peek(&[0x80, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], -2_147_483_648_i32)) + ); + } + + #[test] + fn be_i64_tests() { + assert_parse!( + be_i64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0)) + ); + assert_parse!( + be_i64.parse_peek(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]), + Ok((&b""[..], 9_223_372_036_854_775_807_i64)) + ); + assert_parse!( + be_i64.parse_peek(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]), + Ok((&b""[..], -1)) + ); + assert_parse!( + be_i64.parse_peek(&[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], -9_223_372_036_854_775_808_i64)) + ); + } + + #[test] + fn be_i128_tests() { + assert_parse!( + be_i128.parse_peek( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ][..] + ), + Ok((&b""[..], 0)) + ); + assert_parse!( + be_i128.parse_peek( + &[ + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff + ][..] + ), + Ok(( + &b""[..], + 170_141_183_460_469_231_731_687_303_715_884_105_727_i128 + )) + ); + assert_parse!( + be_i128.parse_peek( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff + ][..] + ), + Ok((&b""[..], -1)) + ); + assert_parse!( + be_i128.parse_peek( + &[ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ][..] + ), + Ok(( + &b""[..], + -170_141_183_460_469_231_731_687_303_715_884_105_728_i128 + )) + ); + } + + #[test] + fn le_i8_tests() { + assert_parse!(le_i8.parse_peek(&[0x00][..]), Ok((&b""[..], 0))); + assert_parse!(le_i8.parse_peek(&[0x7f][..]), Ok((&b""[..], 127))); + assert_parse!(le_i8.parse_peek(&[0xff][..]), Ok((&b""[..], -1))); + assert_parse!(le_i8.parse_peek(&[0x80][..]), Ok((&b""[..], -128))); + } + + #[test] + fn le_i16_tests() { + assert_parse!(le_i16.parse_peek(&[0x00, 0x00][..]), Ok((&b""[..], 0))); + assert_parse!( + le_i16.parse_peek(&[0xff, 0x7f][..]), + Ok((&b""[..], 32_767_i16)) + ); + assert_parse!(le_i16.parse_peek(&[0xff, 0xff][..]), Ok((&b""[..], -1))); + assert_parse!( + le_i16.parse_peek(&[0x00, 0x80][..]), + Ok((&b""[..], -32_768_i16)) + ); + } + + #[test] + fn le_u24_tests() { + assert_parse!( + le_u24.parse_peek(&[0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0)) + ); + assert_parse!( + le_u24.parse_peek(&[0xFF, 0xFF, 0x00][..]), + Ok((&b""[..], 65_535_u32)) + ); + assert_parse!( + le_u24.parse_peek(&[0x56, 0x34, 0x12][..]), + Ok((&b""[..], 1_193_046_u32)) + ); + } + + #[test] + fn le_i24_tests() { + assert_parse!( + le_i24.parse_peek(&[0xFF, 0xFF, 0xFF][..]), + Ok((&b""[..], -1_i32)) + ); + assert_parse!( + le_i24.parse_peek(&[0x00, 0x00, 0xFF][..]), + Ok((&b""[..], -65_536_i32)) + ); + assert_parse!( + le_i24.parse_peek(&[0xAA, 0xCB, 0xED][..]), + Ok((&b""[..], -1_193_046_i32)) + ); + } + + #[test] + fn le_i32_tests() { + assert_parse!( + le_i32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0)) + ); + assert_parse!( + le_i32.parse_peek(&[0xff, 0xff, 0xff, 0x7f][..]), + Ok((&b""[..], 2_147_483_647_i32)) + ); + assert_parse!( + le_i32.parse_peek(&[0xff, 0xff, 0xff, 0xff][..]), + Ok((&b""[..], -1)) + ); + assert_parse!( + le_i32.parse_peek(&[0x00, 0x00, 0x00, 0x80][..]), + Ok((&b""[..], -2_147_483_648_i32)) + ); + } + + #[test] + fn le_i64_tests() { + assert_parse!( + le_i64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0)) + ); + assert_parse!( + le_i64.parse_peek(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f][..]), + Ok((&b""[..], 9_223_372_036_854_775_807_i64)) + ); + assert_parse!( + le_i64.parse_peek(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]), + Ok((&b""[..], -1)) + ); + assert_parse!( + le_i64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80][..]), + Ok((&b""[..], -9_223_372_036_854_775_808_i64)) + ); + } + + #[test] + fn le_i128_tests() { + assert_parse!( + le_i128.parse_peek( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ][..] + ), + Ok((&b""[..], 0)) + ); + assert_parse!( + le_i128.parse_peek( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x7f + ][..] + ), + Ok(( + &b""[..], + 170_141_183_460_469_231_731_687_303_715_884_105_727_i128 + )) + ); + assert_parse!( + le_i128.parse_peek( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff + ][..] + ), + Ok((&b""[..], -1)) + ); + assert_parse!( + le_i128.parse_peek( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80 + ][..] + ), + Ok(( + &b""[..], + -170_141_183_460_469_231_731_687_303_715_884_105_728_i128 + )) + ); + } + + #[test] + fn be_f32_tests() { + assert_parse!( + be_f32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0_f32)) + ); + assert_parse!( + be_f32.parse_peek(&[0x4d, 0x31, 0x1f, 0xd8][..]), + Ok((&b""[..], 185_728_380_f32)) + ); + } + + #[test] + fn be_f64_tests() { + assert_parse!( + be_f64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0_f64)) + ); + assert_parse!( + be_f64.parse_peek(&[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 185_728_392_f64)) + ); + } + + #[test] + fn le_f32_tests() { + assert_parse!( + le_f32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0_f32)) + ); + assert_parse!( + le_f32.parse_peek(&[0xd8, 0x1f, 0x31, 0x4d][..]), + Ok((&b""[..], 185_728_380_f32)) + ); + } + + #[test] + fn le_f64_tests() { + assert_parse!( + le_f64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), + Ok((&b""[..], 0_f64)) + ); + assert_parse!( + le_f64.parse_peek(&[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41][..]), + Ok((&b""[..], 185_728_392_f64)) + ); + } + + #[test] + fn configurable_endianness() { + use crate::binary::Endianness; + + fn be_tst16(i: &[u8]) -> IResult<&[u8], u16> { + u16(Endianness::Big).parse_peek(i) + } + fn le_tst16(i: &[u8]) -> IResult<&[u8], u16> { + u16(Endianness::Little).parse_peek(i) + } + assert_eq!(be_tst16(&[0x80, 0x00]), Ok((&b""[..], 32_768_u16))); + assert_eq!(le_tst16(&[0x80, 0x00]), Ok((&b""[..], 128_u16))); + + fn be_tst32(i: &[u8]) -> IResult<&[u8], u32> { + u32(Endianness::Big).parse_peek(i) + } + fn le_tst32(i: &[u8]) -> IResult<&[u8], u32> { + u32(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tst32(&[0x12, 0x00, 0x60, 0x00]), + Ok((&b""[..], 302_014_464_u32)) + ); + assert_eq!( + le_tst32(&[0x12, 0x00, 0x60, 0x00]), + Ok((&b""[..], 6_291_474_u32)) + ); + + fn be_tst64(i: &[u8]) -> IResult<&[u8], u64> { + u64(Endianness::Big).parse_peek(i) + } + fn le_tst64(i: &[u8]) -> IResult<&[u8], u64> { + u64(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), + Ok((&b""[..], 1_297_142_246_100_992_000_u64)) + ); + assert_eq!( + le_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), + Ok((&b""[..], 36_028_874_334_666_770_u64)) + ); + + fn be_tsti16(i: &[u8]) -> IResult<&[u8], i16> { + i16(Endianness::Big).parse_peek(i) + } + fn le_tsti16(i: &[u8]) -> IResult<&[u8], i16> { + i16(Endianness::Little).parse_peek(i) + } + assert_eq!(be_tsti16(&[0x00, 0x80]), Ok((&b""[..], 128_i16))); + assert_eq!(le_tsti16(&[0x00, 0x80]), Ok((&b""[..], -32_768_i16))); + + fn be_tsti32(i: &[u8]) -> IResult<&[u8], i32> { + i32(Endianness::Big).parse_peek(i) + } + fn le_tsti32(i: &[u8]) -> IResult<&[u8], i32> { + i32(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tsti32(&[0x00, 0x12, 0x60, 0x00]), + Ok((&b""[..], 1_204_224_i32)) + ); + assert_eq!( + le_tsti32(&[0x00, 0x12, 0x60, 0x00]), + Ok((&b""[..], 6_296_064_i32)) + ); + + fn be_tsti64(i: &[u8]) -> IResult<&[u8], i64> { + i64(Endianness::Big).parse_peek(i) + } + fn le_tsti64(i: &[u8]) -> IResult<&[u8], i64> { + i64(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), + Ok((&b""[..], 71_881_672_479_506_432_i64)) + ); + assert_eq!( + le_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]), + Ok((&b""[..], 36_028_874_334_732_032_i64)) + ); + } +} + +mod partial { + use super::*; + use crate::error::ErrMode; + use crate::error::InputError; + use crate::error::Needed; + #[cfg(feature = "alloc")] + use crate::lib::std::vec::Vec; + use crate::Partial; + use crate::{ + ascii::digit1 as digit, + binary::{be_u16, be_u8}, + error::ErrorKind, + lib::std::str::{self, FromStr}, + IResult, + }; + + macro_rules! assert_parse( + ($left: expr, $right: expr) => { + let res: $crate::IResult<_, _, InputError<_>> = $left; + assert_eq!(res, $right); + }; + ); + + #[test] + fn i8_tests() { + assert_parse!( + be_i8.parse_peek(Partial::new(&[0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + be_i8.parse_peek(Partial::new(&[0x7f][..])), + Ok((Partial::new(&b""[..]), 127)) + ); + assert_parse!( + be_i8.parse_peek(Partial::new(&[0xff][..])), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + be_i8.parse_peek(Partial::new(&[0x80][..])), + Ok((Partial::new(&b""[..]), -128)) + ); + assert_parse!( + be_i8.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn i16_tests() { + assert_parse!( + be_i16.parse_peek(Partial::new(&[0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + be_i16.parse_peek(Partial::new(&[0x7f, 0xff][..])), + Ok((Partial::new(&b""[..]), 32_767_i16)) + ); + assert_parse!( + be_i16.parse_peek(Partial::new(&[0xff, 0xff][..])), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + be_i16.parse_peek(Partial::new(&[0x80, 0x00][..])), + Ok((Partial::new(&b""[..]), -32_768_i16)) + ); + assert_parse!( + be_i16.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_parse!( + be_i16.parse_peek(Partial::new(&[0x00][..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn u24_tests() { + assert_parse!( + be_u24.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + be_u24.parse_peek(Partial::new(&[0x00, 0xFF, 0xFF][..])), + Ok((Partial::new(&b""[..]), 65_535_u32)) + ); + assert_parse!( + be_u24.parse_peek(Partial::new(&[0x12, 0x34, 0x56][..])), + Ok((Partial::new(&b""[..]), 1_193_046_u32)) + ); + assert_parse!( + be_u24.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_parse!( + be_u24.parse_peek(Partial::new(&[0x00][..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_parse!( + be_u24.parse_peek(Partial::new(&[0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn i24_tests() { + assert_parse!( + be_i24.parse_peek(Partial::new(&[0xFF, 0xFF, 0xFF][..])), + Ok((Partial::new(&b""[..]), -1_i32)) + ); + assert_parse!( + be_i24.parse_peek(Partial::new(&[0xFF, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), -65_536_i32)) + ); + assert_parse!( + be_i24.parse_peek(Partial::new(&[0xED, 0xCB, 0xAA][..])), + Ok((Partial::new(&b""[..]), -1_193_046_i32)) + ); + assert_parse!( + be_i24.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_parse!( + be_i24.parse_peek(Partial::new(&[0x00][..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_parse!( + be_i24.parse_peek(Partial::new(&[0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn i32_tests() { + assert_parse!( + be_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[0x7f, 0xff, 0xff, 0xff][..])), + Ok((Partial::new(&b""[..]), 2_147_483_647_i32)) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0xff][..])), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[0x80, 0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), -2_147_483_648_i32)) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(4))) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[0x00][..])), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_parse!( + be_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn i64_tests() { + assert_parse!( + be_i64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + be_i64.parse_peek(Partial::new( + &[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..] + )), + Ok((Partial::new(&b""[..]), 9_223_372_036_854_775_807_i64)) + ); + assert_parse!( + be_i64.parse_peek(Partial::new( + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..] + )), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + be_i64.parse_peek(Partial::new( + &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Ok((Partial::new(&b""[..]), -9_223_372_036_854_775_808_i64)) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(8))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[0x00][..])), + Err(ErrMode::Incomplete(Needed::new(7))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(6))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(5))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(4))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_parse!( + be_i64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn i128_tests() { + assert_parse!( + be_i128.parse_peek(Partial::new( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ][..] + )), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[ + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff + ][..] + )), + Ok(( + Partial::new(&b""[..]), + 170_141_183_460_469_231_731_687_303_715_884_105_727_i128 + )) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff + ][..] + )), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ][..] + )), + Ok(( + Partial::new(&b""[..]), + -170_141_183_460_469_231_731_687_303_715_884_105_728_i128 + )) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[][..])), + Err(ErrMode::Incomplete(Needed::new(16))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[0x00][..])), + Err(ErrMode::Incomplete(Needed::new(15))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(14))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(13))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(12))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(11))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), + Err(ErrMode::Incomplete(Needed::new(10))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(9))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(8))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(7))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(6))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(5))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(4))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 + ][..] + )), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_parse!( + be_i128.parse_peek(Partial::new( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + ][..] + )), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + } + + #[test] + fn le_i8_tests() { + assert_parse!( + le_i8.parse_peek(Partial::new(&[0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + le_i8.parse_peek(Partial::new(&[0x7f][..])), + Ok((Partial::new(&b""[..]), 127)) + ); + assert_parse!( + le_i8.parse_peek(Partial::new(&[0xff][..])), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + le_i8.parse_peek(Partial::new(&[0x80][..])), + Ok((Partial::new(&b""[..]), -128)) + ); + } + + #[test] + fn le_i16_tests() { + assert_parse!( + le_i16.parse_peek(Partial::new(&[0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + le_i16.parse_peek(Partial::new(&[0xff, 0x7f][..])), + Ok((Partial::new(&b""[..]), 32_767_i16)) + ); + assert_parse!( + le_i16.parse_peek(Partial::new(&[0xff, 0xff][..])), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + le_i16.parse_peek(Partial::new(&[0x00, 0x80][..])), + Ok((Partial::new(&b""[..]), -32_768_i16)) + ); + } + + #[test] + fn le_u24_tests() { + assert_parse!( + le_u24.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + le_u24.parse_peek(Partial::new(&[0xFF, 0xFF, 0x00][..])), + Ok((Partial::new(&b""[..]), 65_535_u32)) + ); + assert_parse!( + le_u24.parse_peek(Partial::new(&[0x56, 0x34, 0x12][..])), + Ok((Partial::new(&b""[..]), 1_193_046_u32)) + ); + } + + #[test] + fn le_i24_tests() { + assert_parse!( + le_i24.parse_peek(Partial::new(&[0xFF, 0xFF, 0xFF][..])), + Ok((Partial::new(&b""[..]), -1_i32)) + ); + assert_parse!( + le_i24.parse_peek(Partial::new(&[0x00, 0x00, 0xFF][..])), + Ok((Partial::new(&b""[..]), -65_536_i32)) + ); + assert_parse!( + le_i24.parse_peek(Partial::new(&[0xAA, 0xCB, 0xED][..])), + Ok((Partial::new(&b""[..]), -1_193_046_i32)) + ); + } + + #[test] + fn le_i32_tests() { + assert_parse!( + le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + le_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0x7f][..])), + Ok((Partial::new(&b""[..]), 2_147_483_647_i32)) + ); + assert_parse!( + le_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0xff][..])), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x80][..])), + Ok((Partial::new(&b""[..]), -2_147_483_648_i32)) + ); + } + + #[test] + fn le_i64_tests() { + assert_parse!( + le_i64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + le_i64.parse_peek(Partial::new( + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f][..] + )), + Ok((Partial::new(&b""[..]), 9_223_372_036_854_775_807_i64)) + ); + assert_parse!( + le_i64.parse_peek(Partial::new( + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..] + )), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + le_i64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80][..] + )), + Ok((Partial::new(&b""[..]), -9_223_372_036_854_775_808_i64)) + ); + } + + #[test] + fn le_i128_tests() { + assert_parse!( + le_i128.parse_peek(Partial::new( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ][..] + )), + Ok((Partial::new(&b""[..]), 0)) + ); + assert_parse!( + le_i128.parse_peek(Partial::new( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x7f + ][..] + )), + Ok(( + Partial::new(&b""[..]), + 170_141_183_460_469_231_731_687_303_715_884_105_727_i128 + )) + ); + assert_parse!( + le_i128.parse_peek(Partial::new( + &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff + ][..] + )), + Ok((Partial::new(&b""[..]), -1)) + ); + assert_parse!( + le_i128.parse_peek(Partial::new( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80 + ][..] + )), + Ok(( + Partial::new(&b""[..]), + -170_141_183_460_469_231_731_687_303_715_884_105_728_i128 + )) + ); + } + + #[test] + fn be_f32_tests() { + assert_parse!( + be_f32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0_f32)) + ); + assert_parse!( + be_f32.parse_peek(Partial::new(&[0x4d, 0x31, 0x1f, 0xd8][..])), + Ok((Partial::new(&b""[..]), 185_728_380_f32)) + ); + } + + #[test] + fn be_f64_tests() { + assert_parse!( + be_f64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Ok((Partial::new(&b""[..]), 0_f64)) + ); + assert_parse!( + be_f64.parse_peek(Partial::new( + &[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00][..] + )), + Ok((Partial::new(&b""[..]), 185_728_392_f64)) + ); + } + + #[test] + fn le_f32_tests() { + assert_parse!( + le_f32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), + Ok((Partial::new(&b""[..]), 0_f32)) + ); + assert_parse!( + le_f32.parse_peek(Partial::new(&[0xd8, 0x1f, 0x31, 0x4d][..])), + Ok((Partial::new(&b""[..]), 185_728_380_f32)) + ); + } + + #[test] + fn le_f64_tests() { + assert_parse!( + le_f64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] + )), + Ok((Partial::new(&b""[..]), 0_f64)) + ); + assert_parse!( + le_f64.parse_peek(Partial::new( + &[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41][..] + )), + Ok((Partial::new(&b""[..]), 185_728_392_f64)) + ); + } + + #[test] + fn configurable_endianness() { + use crate::binary::Endianness; + + fn be_tst16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> { + u16(Endianness::Big).parse_peek(i) + } + fn le_tst16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> { + u16(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tst16(Partial::new(&[0x80, 0x00])), + Ok((Partial::new(&b""[..]), 32_768_u16)) + ); + assert_eq!( + le_tst16(Partial::new(&[0x80, 0x00])), + Ok((Partial::new(&b""[..]), 128_u16)) + ); + + fn be_tst32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { + u32(Endianness::Big).parse_peek(i) + } + fn le_tst32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { + u32(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tst32(Partial::new(&[0x12, 0x00, 0x60, 0x00])), + Ok((Partial::new(&b""[..]), 302_014_464_u32)) + ); + assert_eq!( + le_tst32(Partial::new(&[0x12, 0x00, 0x60, 0x00])), + Ok((Partial::new(&b""[..]), 6_291_474_u32)) + ); + + fn be_tst64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> { + u64(Endianness::Big).parse_peek(i) + } + fn le_tst64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> { + u64(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tst64(Partial::new(&[ + 0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00 + ])), + Ok((Partial::new(&b""[..]), 1_297_142_246_100_992_000_u64)) + ); + assert_eq!( + le_tst64(Partial::new(&[ + 0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00 + ])), + Ok((Partial::new(&b""[..]), 36_028_874_334_666_770_u64)) + ); + + fn be_tsti16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> { + i16(Endianness::Big).parse_peek(i) + } + fn le_tsti16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> { + i16(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tsti16(Partial::new(&[0x00, 0x80])), + Ok((Partial::new(&b""[..]), 128_i16)) + ); + assert_eq!( + le_tsti16(Partial::new(&[0x00, 0x80])), + Ok((Partial::new(&b""[..]), -32_768_i16)) + ); + + fn be_tsti32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> { + i32(Endianness::Big).parse_peek(i) + } + fn le_tsti32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> { + i32(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tsti32(Partial::new(&[0x00, 0x12, 0x60, 0x00])), + Ok((Partial::new(&b""[..]), 1_204_224_i32)) + ); + assert_eq!( + le_tsti32(Partial::new(&[0x00, 0x12, 0x60, 0x00])), + Ok((Partial::new(&b""[..]), 6_296_064_i32)) + ); + + fn be_tsti64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> { + i64(Endianness::Big).parse_peek(i) + } + fn le_tsti64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> { + i64(Endianness::Little).parse_peek(i) + } + assert_eq!( + be_tsti64(Partial::new(&[ + 0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00 + ])), + Ok((Partial::new(&b""[..]), 71_881_672_479_506_432_i64)) + ); + assert_eq!( + le_tsti64(Partial::new(&[ + 0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00 + ])), + Ok((Partial::new(&b""[..]), 36_028_874_334_732_032_i64)) + ); + } + + #[test] + #[cfg(feature = "alloc")] + fn length_count_test() { + fn number(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { + digit + .try_map(str::from_utf8) + .try_map(FromStr::from_str) + .parse_peek(i) + } + + fn cnt(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + length_count(unpeek(number), "abc").parse_peek(i) + } + + assert_eq!( + cnt(Partial::new(&b"2abcabcabcdef"[..])), + Ok((Partial::new(&b"abcdef"[..]), vec![&b"abc"[..], &b"abc"[..]])) + ); + assert_eq!( + cnt(Partial::new(&b"2ab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + cnt(Partial::new(&b"3abcab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + cnt(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Slice + ))) + ); + assert_eq!( + cnt(Partial::new(&b"2abcxxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); + } + + #[test] + fn length_data_test() { + fn number(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> { + digit + .try_map(str::from_utf8) + .try_map(FromStr::from_str) + .parse_peek(i) + } + + fn take(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + length_data(unpeek(number)).parse_peek(i) + } + + assert_eq!( + take(Partial::new(&b"6abcabcabcdef"[..])), + Ok((Partial::new(&b"abcdef"[..]), &b"abcabc"[..])) + ); + assert_eq!( + take(Partial::new(&b"3ab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + take(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Slice + ))) + ); + assert_eq!( + take(Partial::new(&b"2abcxxx"[..])), + Ok((Partial::new(&b"cxxx"[..]), &b"ab"[..])) + ); + } + + #[test] + fn length_value_test() { + use crate::stream::StreamIsPartial; + + fn length_value_1(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> { + length_value(be_u8, be_u16).parse_peek(i) + } + fn length_value_2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (u8, u8)> { + length_value(be_u8, (be_u8, be_u8)).parse_peek(i) + } + + let mut empty_complete = Partial::new(&b""[..]); + let _ = empty_complete.complete(); + + let i1 = [0, 5, 6]; + assert_eq!( + length_value_1(Partial::new(&i1)), + Err(ErrMode::Backtrack(error_position!( + &empty_complete, + ErrorKind::Slice + ))) + ); + assert_eq!( + length_value_2(Partial::new(&i1)), + Err(ErrMode::Backtrack(error_position!( + &empty_complete, + ErrorKind::Token + ))) + ); + + let i2 = [1, 5, 6, 3]; + { + let mut middle_complete = Partial::new(&i2[1..2]); + let _ = middle_complete.complete(); + assert_eq!( + length_value_1(Partial::new(&i2)), + Err(ErrMode::Backtrack(error_position!( + &middle_complete, + ErrorKind::Slice + ))) + ); + assert_eq!( + length_value_2(Partial::new(&i2)), + Err(ErrMode::Backtrack(error_position!( + &empty_complete, + ErrorKind::Token + ))) + ); + } + + let i3 = [2, 5, 6, 3, 4, 5, 7]; + assert_eq!( + length_value_1(Partial::new(&i3)), + Ok((Partial::new(&i3[3..]), 1286)) + ); + assert_eq!( + length_value_2(Partial::new(&i3)), + Ok((Partial::new(&i3[3..]), (5, 6))) + ); + + let i4 = [3, 5, 6, 3, 4, 5]; + assert_eq!( + length_value_1(Partial::new(&i4)), + Ok((Partial::new(&i4[4..]), 1286)) + ); + assert_eq!( + length_value_2(Partial::new(&i4)), + Ok((Partial::new(&i4[4..]), (5, 6))) + ); + } +} diff --git a/vendor/winnow/src/combinator/branch.rs b/vendor/winnow/src/combinator/branch.rs new file mode 100644 index 0000000..b909ff1 --- /dev/null +++ b/vendor/winnow/src/combinator/branch.rs @@ -0,0 +1,297 @@ +use crate::error::{ErrMode, ErrorKind, ParserError}; +use crate::stream::Stream; +use crate::trace::trace; +use crate::*; + +#[doc(inline)] +pub use crate::dispatch; + +/// Helper trait for the [alt()] combinator. +/// +/// This trait is implemented for tuples of up to 21 elements +pub trait Alt<I, O, E> { + /// Tests each parser in the tuple and returns the result of the first one that succeeds + fn choice(&mut self, input: &mut I) -> PResult<O, E>; +} + +/// Pick the first successful parser +/// +/// For tight control over the error, add a final case using [`fail`][crate::combinator::fail]. +/// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all +/// errors or return the error of the parser that went the farthest in the input data. +/// +/// When the alternative cases have unique prefixes, [`dispatch`] can offer better performance. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::InputError,error::ErrorKind, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::ascii::{alpha1, digit1}; +/// use winnow::combinator::alt; +/// # fn main() { +/// fn parser(input: &str) -> IResult<&str, &str> { +/// alt((alpha1, digit1)).parse_peek(input) +/// }; +/// +/// // the first parser, alpha1, recognizes the input +/// assert_eq!(parser("abc"), Ok(("", "abc"))); +/// +/// // the first parser returns an error, so alt tries the second one +/// assert_eq!(parser("123456"), Ok(("", "123456"))); +/// +/// // both parsers failed, and with the default error type, alt will return the last error +/// assert_eq!(parser(" "), Err(ErrMode::Backtrack(InputError::new(" ", ErrorKind::Slice)))); +/// # } +/// ``` +#[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)) +} + +/// Helper trait for the [permutation()] combinator. +/// +/// This trait is implemented for tuples of up to 21 elements +pub trait Permutation<I, O, E> { + /// Tries to apply all parsers in the tuple in various orders until all of them succeed + fn permutation(&mut self, input: &mut I) -> PResult<O, E>; +} + +/// Applies a list of parsers in any order. +/// +/// Permutation will succeed if all of the child parsers succeeded. +/// It takes as argument a tuple of parsers, and returns a +/// tuple of the parser results. +/// +/// ```rust +/// # use winnow::{error::ErrMode,error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::ascii::{alpha1, digit1}; +/// use winnow::combinator::permutation; +/// # fn main() { +/// fn parser(input: &str) -> IResult<&str, (&str, &str)> { +/// permutation((alpha1, digit1)).parse_peek(input) +/// } +/// +/// // permutation recognizes alphabetic characters then digit +/// assert_eq!(parser("abc123"), Ok(("", ("abc", "123")))); +/// +/// // but also in inverse order +/// assert_eq!(parser("123abc"), Ok(("", ("abc", "123")))); +/// +/// // it will fail if one of the parsers failed +/// assert_eq!(parser("abc;"), Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Slice)))); +/// # } +/// ``` +/// +/// The parsers are applied greedily: if there are multiple unapplied parsers +/// that could parse the next slice of input, the first one is used. +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; +/// # use winnow::prelude::*; +/// use winnow::combinator::permutation; +/// use winnow::token::any; +/// +/// fn parser(input: &str) -> IResult<&str, (char, char)> { +/// permutation((any, 'a')).parse_peek(input) +/// } +/// +/// // any parses 'b', then char('a') parses 'a' +/// assert_eq!(parser("ba"), Ok(("", ('b', 'a')))); +/// +/// // 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)))); +/// ``` +/// +pub fn permutation<I: Stream, O, E: ParserError<I>, List: Permutation<I, O, E>>( + mut l: List, +) -> impl Parser<I, O, E> { + trace("permutation", move |i: &mut I| l.permutation(i)) +} + +impl<const N: usize, I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for [P; N] { + fn choice(&mut self, input: &mut I) -> PResult<O, E> { + let mut error: Option<E> = None; + + let start = input.checkpoint(); + for branch in self { + input.reset(start.clone()); + 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, ErrorKind::Alt))), + None => Err(ErrMode::assert(input, "`alt` needs at least one parser")), + } + } +} + +macro_rules! alt_trait( + ($first:ident $second:ident $($id: ident)+) => ( + alt_trait!(__impl $first $second; $($id)+); + ); + (__impl $($current:ident)*; $head:ident $($id: ident)+) => ( + alt_trait_impl!($($current)*); + + alt_trait!(__impl $($current)* $head; $($id)+); + ); + (__impl $($current:ident)*; $head:ident) => ( + alt_trait_impl!($($current)*); + alt_trait_impl!($($current)* $head); + ); +); + +macro_rules! alt_trait_impl( + ($($id:ident)+) => ( + impl< + I: Stream, Output, Error: ParserError<I>, + $($id: Parser<I, Output, Error>),+ + > Alt<I, Output, Error> for ( $($id),+ ) { + + fn choice(&mut self, input: &mut I) -> PResult<Output, Error> { + let start = input.checkpoint(); + match self.0.parse_next(input) { + Err(ErrMode::Backtrack(e)) => alt_trait_inner!(1, self, input, start, e, $($id)+), + res => res, + } + } + } + ); +); + +macro_rules! alt_trait_inner( + ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({ + $input.reset($start.clone()); + match $self.$it.parse_next($input) { + Err(ErrMode::Backtrack(e)) => { + let err = $err.or(e); + succ!($it, alt_trait_inner!($self, $input, $start, err, $($id)+)) + } + res => res, + } + }); + ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({ + Err(ErrMode::Backtrack($err.append($input, 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,) { + fn choice(&mut self, input: &mut I) -> PResult<O, E> { + self.0.parse_next(input) + } +} + +macro_rules! permutation_trait( + ( + $name1:ident $ty1:ident $item1:ident + $name2:ident $ty2:ident $item2:ident + $($name3:ident $ty3:ident $item3:ident)* + ) => ( + permutation_trait!(__impl $name1 $ty1 $item1, $name2 $ty2 $item2; $($name3 $ty3 $item3)*); + ); + ( + __impl $($name:ident $ty:ident $item:ident),+; + $name1:ident $ty1:ident $item1:ident $($name2:ident $ty2:ident $item2:ident)* + ) => ( + permutation_trait_impl!($($name $ty $item),+); + permutation_trait!(__impl $($name $ty $item),+ , $name1 $ty1 $item1; $($name2 $ty2 $item2)*); + ); + (__impl $($name:ident $ty:ident $item:ident),+;) => ( + permutation_trait_impl!($($name $ty $item),+); + ); +); + +macro_rules! permutation_trait_impl( + ($($name:ident $ty:ident $item:ident),+) => ( + impl< + I: Stream, $($ty),+ , Error: ParserError<I>, + $($name: Parser<I, $ty, Error>),+ + > Permutation<I, ( $($ty),+ ), Error> for ( $($name),+ ) { + + fn permutation(&mut self, input: &mut I) -> PResult<( $($ty),+ ), Error> { + let mut res = ($(Option::<$ty>::None),+); + + loop { + let mut err: Option<Error> = None; + let start = input.checkpoint(); + permutation_trait_inner!(0, self, input, start, res, err, $($name)+); + + // If we reach here, every iterator has either been applied before, + // 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))); + } + + // All parsers were applied + match res { + ($(Some($item)),+) => return Ok(($($item),+)), + _ => unreachable!(), + } + } + } + } + ); +); + +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()); + match $self.$it.parse_next($input) { + Ok(o) => { + $res.$it = Some(o); + continue; + } + Err(ErrMode::Backtrack(e)) => { + $err = Some(match $err { + Some(err) => err.or(e), + None => e, + }); + } + Err(e) => return Err(e), + }; + } + succ!($it, permutation_trait_inner!($self, $input, $start, $res, $err, $($id)*)); + ); + ($it:tt, $self:expr, $input:ident, $start:ident, $res:expr, $err:expr,) => (); +); + +permutation_trait!( + P1 O1 o1 + P2 O2 o2 + P3 O3 o3 + P4 O4 o4 + P5 O5 o5 + P6 O6 o6 + P7 O7 o7 + P8 O8 o8 + P9 O9 o9 + P10 O10 o10 + P11 O11 o11 + P12 O12 o12 + P13 O13 o13 + P14 O14 o14 + P15 O15 o15 + P16 O16 o16 + P17 O17 o17 + P18 O18 o18 + P19 O19 o19 + P20 O20 o20 + P21 O21 o21 +); diff --git a/vendor/winnow/src/combinator/core.rs b/vendor/winnow/src/combinator/core.rs new file mode 100644 index 0000000..d784b4e --- /dev/null +++ b/vendor/winnow/src/combinator/core.rs @@ -0,0 +1,491 @@ +use crate::error::{ErrMode, ErrorKind, Needed, ParserError}; +use crate::stream::Stream; +use crate::trace::trace; +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(("", ""))); +/// ``` +#[inline] +pub fn rest<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> +where + I: Stream, +{ + trace("rest", move |input: &mut I| Ok(input.finish())).parse_next(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))); +/// ``` +#[inline] +pub fn rest_len<I, E: ParserError<I>>(input: &mut I) -> PResult<usize, E> +where + I: Stream, +{ + trace("rest_len", move |input: &mut I| { + let len = input.eof_offset(); + Ok(len) + }) + .parse_next(input) +} + +/// Apply a [`Parser`], producing `None` on [`ErrMode::Backtrack`]. +/// +/// To chain an error up, see [`cut_err`]. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::prelude::*; +/// use winnow::combinator::opt; +/// use winnow::ascii::alpha1; +/// # fn main() { +/// +/// fn parser(i: &str) -> IResult<&str, Option<&str>> { +/// opt(alpha1).parse_peek(i) +/// } +/// +/// assert_eq!(parser("abcd;"), Ok((";", Some("abcd")))); +/// 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> +where + F: Parser<I, O, E>, +{ + trace("opt", move |input: &mut I| { + let start = input.checkpoint(); + match f.parse_next(input) { + Ok(o) => Ok(Some(o)), + Err(ErrMode::Backtrack(_)) => { + input.reset(start); + Ok(None) + } + Err(e) => Err(e), + } + }) +} + +/// Calls the parser if the condition is met. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, IResult}; +/// # use winnow::prelude::*; +/// use winnow::combinator::cond; +/// use winnow::ascii::alpha1; +/// # fn main() { +/// +/// fn parser(b: bool, i: &str) -> IResult<&str, Option<&str>> { +/// cond(b, alpha1).parse_peek(i) +/// } +/// +/// assert_eq!(parser(true, "abcd;"), Ok((";", Some("abcd")))); +/// assert_eq!(parser(false, "abcd;"), Ok(("abcd;", None))); +/// assert_eq!(parser(true, "123;"), Err(ErrMode::Backtrack(InputError::new("123;", ErrorKind::Slice)))); +/// 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> +where + I: Stream, + F: Parser<I, O, E>, +{ + trace("cond", move |input: &mut I| { + if b { + f.parse_next(input).map(Some) + } else { + Ok(None) + } + }) +} + +/// Tries to apply its parser without consuming the input. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult}; +/// # use winnow::prelude::*; +/// use winnow::combinator::peek; +/// use winnow::ascii::alpha1; +/// # fn main() { +/// +/// let mut parser = peek(alpha1); +/// +/// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", "abcd"))); +/// assert_eq!(parser.parse_peek("123;"), Err(ErrMode::Backtrack(InputError::new("123;", ErrorKind::Slice)))); +/// # } +/// ``` +#[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> +where + F: Parser<I, O, E>, +{ + trace("peek", move |input: &mut I| { + let start = input.checkpoint(); + let res = f.parse_next(input); + input.reset(start); + res + }) +} + +/// Match the end of the [`Stream`] +/// +/// Otherwise, it will error. +/// +/// # Example +/// +/// ```rust +/// # use std::str; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::combinator::eof; +/// # use winnow::prelude::*; +/// +/// let mut parser = eof; +/// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Eof)))); +/// assert_eq!(parser.parse_peek(""), Ok(("", ""))); +/// ``` +#[doc(alias = "end")] +#[doc(alias = "eoi")] +pub fn eof<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E> +where + I: Stream, +{ + trace("eof", move |input: &mut I| { + if input.eof_offset() == 0 { + Ok(input.next_slice(0)) + } else { + Err(ErrMode::from_error_kind(input, ErrorKind::Eof)) + } + }) + .parse_next(input) +} + +/// Succeeds if the child parser returns an error. +/// +/// **Note:** This does not advance the [`Stream`] +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult}; +/// # use winnow::prelude::*; +/// use winnow::combinator::not; +/// use winnow::ascii::alpha1; +/// # fn main() { +/// +/// let mut parser = not(alpha1); +/// +/// assert_eq!(parser.parse_peek("123"), Ok(("123", ()))); +/// 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> +where + F: Parser<I, O, E>, +{ + trace("not", move |input: &mut I| { + let start = input.checkpoint(); + let res = parser.parse_next(input); + input.reset(start); + match res { + Ok(_) => Err(ErrMode::from_error_kind(input, ErrorKind::Not)), + Err(ErrMode::Backtrack(_)) => Ok(()), + Err(e) => Err(e), + } + }) +} + +/// Transforms an [`ErrMode::Backtrack`] (recoverable) to [`ErrMode::Cut`] (unrecoverable) +/// +/// This commits the parse result, preventing alternative branch paths like with +/// [`winnow::combinator::alt`][crate::combinator::alt]. +/// +/// # Example +/// +/// Without `cut_err`: +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::token::one_of; +/// # use winnow::ascii::digit1; +/// # use winnow::combinator::rest; +/// # use winnow::combinator::alt; +/// # use winnow::combinator::preceded; +/// # use winnow::prelude::*; +/// # fn main() { +/// +/// fn parser(input: &str) -> IResult<&str, &str> { +/// alt(( +/// preceded(one_of(['+', '-']), digit1), +/// rest +/// )).parse_peek(input) +/// } +/// +/// assert_eq!(parser("+10 ab"), Ok((" ab", "10"))); +/// assert_eq!(parser("ab"), Ok(("", "ab"))); +/// assert_eq!(parser("+"), Ok(("", "+"))); +/// # } +/// ``` +/// +/// With `cut_err`: +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::prelude::*; +/// # use winnow::token::one_of; +/// # use winnow::ascii::digit1; +/// # use winnow::combinator::rest; +/// # use winnow::combinator::alt; +/// # use winnow::combinator::preceded; +/// use winnow::combinator::cut_err; +/// # fn main() { +/// +/// fn parser(input: &str) -> IResult<&str, &str> { +/// alt(( +/// preceded(one_of(['+', '-']), cut_err(digit1)), +/// rest +/// )).parse_peek(input) +/// } +/// +/// assert_eq!(parser("+10 ab"), Ok((" ab", "10"))); +/// assert_eq!(parser("ab"), Ok(("", "ab"))); +/// 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> +where + I: Stream, + F: Parser<I, O, E>, +{ + trace("cut_err", move |input: &mut I| { + parser.parse_next(input).map_err(|e| e.cut()) + }) +} + +/// Transforms an [`ErrMode::Cut`] (unrecoverable) to [`ErrMode::Backtrack`] (recoverable) +/// +/// 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> +where + I: Stream, + F: Parser<I, O, E>, +{ + trace("backtrack_err", move |input: &mut I| { + parser.parse_next(input).map_err(|e| e.backtrack()) + }) +} + +/// A placeholder for a not-yet-implemented [`Parser`] +/// +/// This is analogous to the [`todo!`] macro and helps with prototyping. +/// +/// # Panic +/// +/// This will panic when parsing +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::combinator::todo; +/// +/// fn parser(input: &mut &str) -> PResult<u64> { +/// todo(input) +/// } +/// ``` +#[track_caller] +pub fn todo<I, O, E>(input: &mut I) -> PResult<O, E> +where + I: Stream, +{ + #![allow(clippy::todo)] + trace("todo", move |_input: &mut I| todo!("unimplemented parse")).parse_next(input) +} + +/// Repeats the embedded parser, lazily returning the results +/// +/// Call the iterator's [`ParserIterator::finish`] method to get the remaining input if successful, +/// or the error value if we encountered an error. +/// +/// On [`ErrMode::Backtrack`], iteration will stop. To instead chain an error up, see [`cut_err`]. +/// +/// # Example +/// +/// ```rust +/// use winnow::{combinator::iterator, IResult, token::tag, ascii::alpha1, combinator::terminated}; +/// use std::collections::HashMap; +/// +/// let data = "abc|defg|hijkl|mnopqr|123"; +/// let mut it = iterator(data, terminated(alpha1, "|")); +/// +/// let parsed = it.map(|v| (v, v.len())).collect::<HashMap<_,_>>(); +/// let res: IResult<_,_> = it.finish(); +/// +/// 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> +where + F: Parser<I, O, E>, + I: Stream, + E: ParserError<I>, +{ + ParserIterator { + parser, + input, + state: Some(State::Running), + o: Default::default(), + } +} + +/// Main structure associated to [`iterator`]. +pub struct ParserIterator<F, I, O, E> +where + F: Parser<I, O, E>, + I: Stream, +{ + parser: F, + input: I, + state: Option<State<E>>, + o: core::marker::PhantomData<O>, +} + +impl<F, I, O, E> ParserIterator<F, I, O, E> +where + F: Parser<I, O, E>, + I: Stream, +{ + /// Returns the remaining input if parsing was successful, or the error if we encountered an error. + pub fn finish(mut self) -> PResult<(I, ()), E> { + match self.state.take().unwrap() { + State::Running | State::Done => Ok((self.input, ())), + State::Failure(e) => Err(ErrMode::Cut(e)), + State::Incomplete(i) => Err(ErrMode::Incomplete(i)), + } + } +} + +impl<'a, F, I, O, E> core::iter::Iterator for &'a mut ParserIterator<F, I, O, E> +where + F: Parser<I, O, E>, + I: Stream, +{ + type Item = O; + + fn next(&mut self) -> Option<Self::Item> { + if let State::Running = self.state.take().unwrap() { + let start = self.input.checkpoint(); + + match self.parser.parse_next(&mut self.input) { + Ok(o) => { + self.state = Some(State::Running); + Some(o) + } + Err(ErrMode::Backtrack(_)) => { + self.input.reset(start); + self.state = Some(State::Done); + None + } + Err(ErrMode::Cut(e)) => { + self.state = Some(State::Failure(e)); + None + } + Err(ErrMode::Incomplete(i)) => { + self.state = Some(State::Incomplete(i)); + None + } + } + } else { + None + } + } +} + +enum State<E> { + Running, + Done, + Failure(E), + Incomplete(Needed), +} + +/// Always succeeds with given value without consuming any input. +/// +/// For example, it can be used as the last alternative in `alt` to +/// specify the default case. +/// +/// **Note:** This never advances the [`Stream`] +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::prelude::*; +/// use winnow::combinator::alt; +/// use winnow::combinator::success; +/// +/// let mut parser = success::<_,_,InputError<_>>(10); +/// assert_eq!(parser.parse_peek("xyz"), Ok(("xyz", 10))); +/// +/// fn sign(input: &str) -> IResult<&str, isize> { +/// alt(( +/// '-'.value(-1), +/// '+'.value(1), +/// success::<_,_,InputError<_>>(1) +/// )).parse_peek(input) +/// } +/// assert_eq!(sign("+10"), Ok(("10", 1))); +/// assert_eq!(sign("-10"), Ok(("10", -1))); +/// assert_eq!(sign("10"), Ok(("10", 1))); +/// ``` +#[doc(alias = "value")] +#[doc(alias = "empty")] +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 +/// control the error message given. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult}; +/// # use winnow::prelude::*; +/// use winnow::combinator::fail; +/// +/// let s = "string"; +/// 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| { + Err(ErrMode::from_error_kind(i, ErrorKind::Fail)) + }) + .parse_next(i) +} diff --git a/vendor/winnow/src/combinator/mod.rs b/vendor/winnow/src/combinator/mod.rs new file mode 100644 index 0000000..3be56bc --- /dev/null +++ b/vendor/winnow/src/combinator/mod.rs @@ -0,0 +1,172 @@ +//! # List of parsers and combinators +//! +//! **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. +//! +//! ## 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". +//! +//! | combinator | usage | input | output | comment | +//! |---|---|---|---|---| +//! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` | `"abc"` | `Ok(("bc", '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"` | `Ok(("yab", 'x'))` |Matches anything but the provided characters| +//! | [`tag`][crate::token::tag] | `"hello"` | `"hello world"` | `Ok((" world", "hello"))` |Recognizes a specific suite of characters or bytes| +//! | [`tag_no_case`][crate::token::tag_no_case] | `tag_no_case("hello")` | `"HeLLo World"` | `Ok((" World", "HeLLo"))` |Case insensitive comparison. Note that case insensitive comparison is not well defined for unicode, and that you might have bad surprises| +//! | [`take`][crate::token::take] | `take(4)` | `"hello"` | `Ok(("o", "hell"))` |Takes a specific number of bytes or characters| +//! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` | `"abc123"` | `Ok(("123", "abc"))` |Returns the longest list of bytes for which the provided pattern matches.| +//! | [`take_till0`][crate::token::take_till0] | `take_till0(is_alphabetic)` | `"123abc"` | `Ok(("abc", "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_until0`][crate::token::take_until0] | `take_until0("world")` | `"Hello world"` | `Ok(("world", "Hello "))` |Returns the longest list of bytes or characters until the provided tag is found. `take_until1` does the same, but must return at least one character| +//! +//! ## Choice combinators +//! +//! | combinator | usage | input | output | comment | +//! |---|---|---|---|---| +//! | [`alt`] | `alt(("ab", "cd"))` | `"cdef"` | `Ok(("ef", "cd"))` |Try a list of parsers and return the result of the first successful one| +//! | [`dispatch`] | \- | \- | \- | `match` for parsers | +//! | [`permutation`] | `permutation(("ab", "cd", "12"))` | `"cd12abc"` | `Ok(("c", ("ab", "cd", "12"))` |Succeeds when all its child parser have succeeded, whatever the order| +//! +//! ## Sequence combinators +//! +//! | combinator | usage | 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| +//! | [`delimited`] | `delimited(char('('), take(2), char(')'))` | `"(ab)cd"` | `Ok(("cd", "ab"))` || +//! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `Ok(("Z", "XY"))` || +//! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `Ok(("Z", "ab"))` || +//! | [`separated_pair`] | `separated_pair("hello", char(','), "world")` | `"hello,world!"` | `Ok(("!", ("hello", "world")))` || +//! +//! ## Applying a parser multiple times +//! +//! | combinator | usage | input | output | comment | +//! |---|---|---|---|---| +//! | [`repeat`] | `repeat(1..=3, "ab")` | `"ababc"` | `Ok(("c", vec!["ab", "ab"]))` |Applies the parser between m and n times (n included) and returns the list of results in a Vec| +//! | [`repeat_till0`] | `repeat_till0(tag( "ab" ), tag( "ef" ))` | `"ababefg"` | `Ok(("g", (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| +//! | [`separated0`] | `separated0("ab", ",")` | `"ab,ab,ab."` | `Ok((".", vec!["ab", "ab", "ab"]))` |`separated1` works like `separated0` but must returns at least one element| +//! | [`fold_repeat`] | `fold_repeat(1..=2, be_u8, \|\| 0, \|acc, item\| acc + item)` | `[1, 2, 3]` | `Ok(([3], 3))` |Applies the parser between m and n times (n included) and folds the list of return value| +//! +//! ## Partial related +//! +//! - [`eof`]: Returns its input if it is at the end of input data +//! - [`Parser::complete_err`]: Replaces an `Incomplete` returned by the child parser with an `Backtrack` +//! +//! ## Modifiers +//! +//! - [`cond`]: Conditional combinator. Wraps another parser and calls it if the condition is met +//! - [`Parser::flat_map`]: method to map a new parser from the output of the first parser, then apply that parser over the rest of the input +//! - [`Parser::value`]: method to replace the result of a parser +//! - [`Parser::map`]: method to map a function on the result of a parser +//! - [`Parser::and_then`]: Applies a second parser over the output of the first one +//! - [`Parser::verify_map`]: Maps a function returning an `Option` on the output of a parser +//! - [`Parser::try_map`]: Maps a function returning a `Result` on the output of a parser +//! - [`Parser::parse_to`]: Apply [`std::str::FromStr`] to the output of the parser +//! - [`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::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 +//! +//! ## Error management and debugging +//! +//! - [`cut_err`]: Commit the parse result, disallowing alternative parsers from being attempted +//! - [`backtrack_err`]: Attemmpts a parse, allowing alternative parsers to be attempted despite +//! use of `cut_err` +//! - [`Parser::context`]: Add context to the error if the parser fails +//! - [`trace`][crate::trace::trace]: Print the parse state with the `debug` feature flag +//! - [`todo()`]: Placeholder parser +//! +//! ## Remaining combinators +//! +//! - [`success`]: Returns a value without consuming any input, always succeeds +//! - [`fail`]: Inversion of `success`. Always fails. +//! - [`Parser::by_ref`]: Allow moving `&mut impl Parser` into other parsers +//! +//! ## Text parsing +//! +//! - [`any`][crate::token::any]: Matches one token +//! - [`tab`][crate::ascii::tab]: Matches a tab character `\t` +//! - [`crlf`][crate::ascii::crlf]: Recognizes the string `\r\n` +//! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`) +//! - [`newline`][crate::ascii::newline]: Matches a newline character `\n` +//! - [`not_line_ending`][crate::ascii::not_line_ending]: Recognizes a string of any char except `\r` or `\n` +//! - [`rest`]: Return the remaining input +//! +//! - [`alpha0`][crate::ascii::alpha0]: Recognizes zero or more lowercase and uppercase alphabetic characters: `[a-zA-Z]`. [`alpha1`][crate::ascii::alpha1] does the same but returns at least one character +//! - [`alphanumeric0`][crate::ascii::alphanumeric0]: Recognizes zero or more numerical and alphabetic characters: `[0-9a-zA-Z]`. [`alphanumeric1`][crate::ascii::alphanumeric1] does the same but returns at least one character +//! - [`space0`][crate::ascii::space0]: Recognizes zero or more spaces and tabs. [`space1`][crate::ascii::space1] does the same but returns at least one character +//! - [`multispace0`][crate::ascii::multispace0]: Recognizes zero or more spaces, tabs, carriage returns and line feeds. [`multispace1`][crate::ascii::multispace1] does the same but returns at least one character +//! - [`digit0`][crate::ascii::digit0]: Recognizes zero or more numerical characters: `[0-9]`. [`digit1`][crate::ascii::digit1] does the same but returns at least one character +//! - [`hex_digit0`][crate::ascii::hex_digit0]: Recognizes zero or more hexadecimal numerical characters: `[0-9A-Fa-f]`. [`hex_digit1`][crate::ascii::hex_digit1] does the same but returns at least one character +//! - [`oct_digit0`][crate::ascii::oct_digit0]: Recognizes zero or more octal characters: `[0-7]`. [`oct_digit1`][crate::ascii::oct_digit1] does the same but returns at least one character +//! +//! - [`float`][crate::ascii::float]: Parse a floating point number in a byte string +//! - [`dec_int`][crate::ascii::dec_uint]: Decode a variable-width, decimal signed integer +//! - [`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 +//! +//! ### Character test functions +//! +//! Use these functions with a combinator like `take_while`: +//! +//! - [`AsChar::is_alpha`][crate::stream::AsChar::is_alpha]: Tests if byte is ASCII alphabetic: `[A-Za-z]` +//! - [`AsChar::is_alphanum`][crate::stream::AsChar::is_alphanum]: Tests if byte is ASCII alphanumeric: `[A-Za-z0-9]` +//! - [`AsChar::is_dec_digit`][crate::stream::AsChar::is_dec_digit]: Tests if byte is ASCII digit: `[0-9]` +//! - [`AsChar::is_hex_digit`][crate::stream::AsChar::is_hex_digit]: Tests if byte is ASCII hex digit: `[0-9A-Fa-f]` +//! - [`AsChar::is_oct_digit`][crate::stream::AsChar::is_oct_digit]: Tests if byte is ASCII octal digit: `[0-7]` +//! - [`AsChar::is_space`][crate::stream::AsChar::is_space]: Tests if byte is ASCII space or tab: `[ \t]` +//! - [`AsChar::is_newline`][crate::stream::AsChar::is_newline]: Tests if byte is ASCII newline: `[\n]` +//! +//! ## 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 +//! +//! ### Integers +//! +//! Parsing integers from binary formats can be done in two ways: With parser functions, or combinators with configurable endianness. +//! +//! - **configurable endianness:** [`i16`][crate::binary::i16], [`i32`][crate::binary::i32], +//! [`i64`][crate::binary::i64], [`u16`][crate::binary::u16], [`u32`][crate::binary::u32], +//! [`u64`][crate::binary::u64] are combinators that take as argument a +//! [`winnow::binary::Endianness`][crate::binary::Endianness], like this: `i16(endianness)`. If the +//! parameter is `winnow::binary::Endianness::Big`, parse a big endian `i16` integer, otherwise a +//! little endian `i16` integer. +//! - **fixed endianness**: The functions are prefixed by `be_` for big endian numbers, and by `le_` for little endian numbers, and the suffix is the type they parse to. As an example, `be_u32` parses a big endian unsigned integer stored in 32 bits. +//! - [`be_f32`][crate::binary::be_f32], [`be_f64`][crate::binary::be_f64]: Big endian floating point numbers +//! - [`le_f32`][crate::binary::le_f32], [`le_f64`][crate::binary::le_f64]: Little endian floating point numbers +//! - [`be_i8`][crate::binary::be_i8], [`be_i16`][crate::binary::be_i16], [`be_i24`][crate::binary::be_i24], [`be_i32`][crate::binary::be_i32], [`be_i64`][crate::binary::be_i64], [`be_i128`][crate::binary::be_i128]: Big endian signed integers +//! - [`be_u8`][crate::binary::be_u8], [`be_u16`][crate::binary::be_u16], [`be_u24`][crate::binary::be_u24], [`be_u32`][crate::binary::be_u32], [`be_u64`][crate::binary::be_u64], [`be_u128`][crate::binary::be_u128]: Big endian unsigned integers +//! - [`le_i8`][crate::binary::le_i8], [`le_i16`][crate::binary::le_i16], [`le_i24`][crate::binary::le_i24], [`le_i32`][crate::binary::le_i32], [`le_i64`][crate::binary::le_i64], [`le_i128`][crate::binary::le_i128]: Little endian signed integers +//! - [`le_u8`][crate::binary::le_u8], [`le_u16`][crate::binary::le_u16], [`le_u24`][crate::binary::le_u24], [`le_u32`][crate::binary::le_u32], [`le_u64`][crate::binary::le_u64], [`le_u128`][crate::binary::le_u128]: Little endian unsigned integers +//! +//! ### Bit stream parsing +//! +//! - [`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 its +//! - [`tag`][crate::binary::bits::tag]: Check if a set number of bis matches a pattern +//! - [`bool`][crate::binary::bits::bool]: Match any one bit + +mod branch; +mod core; +mod multi; +mod parser; +mod sequence; + +#[cfg(test)] +mod tests; + +pub use self::branch::*; +pub use self::core::*; +pub use self::multi::*; +pub use self::parser::*; +pub use self::sequence::*; + +#[allow(unused_imports)] +use crate::Parser; diff --git a/vendor/winnow/src/combinator/multi.rs b/vendor/winnow/src/combinator/multi.rs new file mode 100644 index 0000000..913b6d9 --- /dev/null +++ b/vendor/winnow/src/combinator/multi.rs @@ -0,0 +1,945 @@ +//! Combinators applying their child parser multiple times + +use crate::error::ErrMode; +use crate::error::ErrorKind; +use crate::error::ParserError; +use crate::stream::Accumulate; +use crate::stream::Range; +use crate::stream::Stream; +use crate::trace::trace; +use crate::PResult; +use crate::Parser; + +/// [`Accumulate`] the output of a parser into a container, like `Vec` +/// +/// 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 recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`]. +/// +/// **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. +/// +/// # Example +/// +/// Zero or more reptitions: +/// ```rust +/// # #[cfg(feature = "std")] { +/// # 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) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); +/// assert_eq!(parser("123123"), Ok(("123123", vec![]))); +/// assert_eq!(parser(""), Ok(("", vec![]))); +/// # } +/// ``` +/// +/// One or more reptitions: +/// ```rust +/// # #[cfg(feature = "std")] { +/// # 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) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); +/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag)))); +/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); +/// # } +/// ``` +/// +/// Fixed number of repeitions: +/// ```rust +/// # #[cfg(feature = "std")] { +/// # 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) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); +/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag)))); +/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); +/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); +/// # } +/// ``` +/// +/// Arbitrary reptitions: +/// ```rust +/// # #[cfg(feature = "std")] { +/// # 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) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); +/// assert_eq!(parser("123123"), Ok(("123123", vec![]))); +/// assert_eq!(parser(""), Ok(("", vec![]))); +/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); +/// # } +/// ``` +#[doc(alias = "many0")] +#[doc(alias = "count")] +#[doc(alias = "many0_count")] +#[doc(alias = "many1")] +#[doc(alias = "many1_count")] +#[doc(alias = "many_m_n")] +#[doc(alias = "repeated")] +#[doc(alias = "skip_many")] +#[doc(alias = "skip_many1")] +#[inline(always)] +pub fn repeat<I, O, C, E, F>(range: impl Into<Range>, mut f: F) -> impl Parser<I, C, E> +where + I: Stream, + C: Accumulate<O>, + F: Parser<I, O, E>, + E: ParserError<I>, +{ + let Range { + start_inclusive, + end_inclusive, + } = range.into(); + trace("repeat", move |i: &mut I| { + match (start_inclusive, end_inclusive) { + (0, None) => repeat0_(&mut f, i), + (1, None) => repeat1_(&mut f, i), + (start, end) if Some(start) == end => repeat_n_(start, &mut f, i), + (start, end) => repeat_m_n_(start, end.unwrap_or(usize::MAX), &mut f, i), + } + }) +} + +fn repeat0_<I, O, C, E, F>(f: &mut F, i: &mut I) -> PResult<C, E> +where + I: Stream, + C: Accumulate<O>, + F: Parser<I, O, E>, + E: ParserError<I>, +{ + let mut acc = C::initial(None); + loop { + let start = i.checkpoint(); + let len = i.eof_offset(); + match f.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(acc); + } + Err(e) => return Err(e), + 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")); + } + + acc.accumulate(o); + } + } + } +} + +fn repeat1_<I, O, C, E, F>(f: &mut F, i: &mut I) -> PResult<C, E> +where + I: Stream, + C: Accumulate<O>, + F: Parser<I, O, E>, + E: ParserError<I>, +{ + match f.parse_next(i) { + Err(e) => Err(e.append(i, ErrorKind::Many)), + Ok(o) => { + let mut acc = C::initial(None); + acc.accumulate(o); + + loop { + let start = i.checkpoint(); + let len = i.eof_offset(); + match f.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(acc); + } + Err(e) => return Err(e), + 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")); + } + + acc.accumulate(o); + } + } + } + } + } +} + +/// [`Accumulate`] the output of parser `f` into a container, like `Vec`, until the parser `g` +/// produces a result. +/// +/// Returns a tuple of the results of `f` in a `Vec` and the result of `g`. +/// +/// `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`]. +/// +/// # Example +/// +/// ```rust +/// # #[cfg(feature = "std")] { +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::combinator::repeat_till0; +/// use winnow::token::tag; +/// +/// fn parser(s: &str) -> IResult<&str, (Vec<&str>, &str)> { +/// repeat_till0("abc", "end").parse_peek(s) +/// }; +/// +/// assert_eq!(parser("abcabcend"), Ok(("", (vec!["abc", "abc"], "end")))); +/// assert_eq!(parser("abc123end"), Err(ErrMode::Backtrack(InputError::new("123end", ErrorKind::Tag)))); +/// assert_eq!(parser("123123end"), Err(ErrMode::Backtrack(InputError::new("123123end", ErrorKind::Tag)))); +/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); +/// assert_eq!(parser("abcendefg"), Ok(("efg", (vec!["abc"], "end")))); +/// # } +/// ``` +#[doc(alias = "many_till0")] +pub fn repeat_till0<I, O, C, P, E, F, G>(mut f: F, mut 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>, +{ + trace("repeat_till0", move |i: &mut I| { + let mut res = C::initial(None); + loop { + let start = i.checkpoint(); + let len = i.eof_offset(); + match g.parse_next(i) { + Ok(o) => return Ok((res, o)), + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + match f.parse_next(i) { + Err(e) => return Err(e.append(i, ErrorKind::Many)), + 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), + } + } + }) +} + +/// [`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")] +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| { + let mut res = C::initial(None); + + let start = i.checkpoint(); + match parser.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(res); + } + Err(e) => return Err(e), + Ok(o) => { + res.accumulate(o); + } + } + + loop { + let start = i.checkpoint(); + let len = i.eof_offset(); + match sep.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(res); + } + Err(e) => return Err(e), + Ok(_) => { + // infinite loop check: the parser must always consume + if i.eof_offset() == len { + return Err(ErrMode::assert(i, "sep parsers must always consume")); + } + + match parser.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(res); + } + Err(e) => return Err(e), + Ok(o) => { + res.accumulate(o); + } + } + } + } + } + }) +} + +/// [`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")] +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| { + let mut res = C::initial(None); + + // Parse the first element + match parser.parse_next(i) { + Err(e) => return Err(e), + Ok(o) => { + res.accumulate(o); + } + } + + loop { + let start = i.checkpoint(); + let len = i.eof_offset(); + match sep.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(res); + } + Err(e) => return Err(e), + Ok(_) => { + // infinite loop check: the parser must always consume + if i.eof_offset() == len { + return Err(ErrMode::assert(i, "sep parsers must always consume")); + } + + match parser.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(res); + } + Err(e) => return Err(e), + Ok(o) => { + res.accumulate(o); + } + } + } + } + } + }) +} + +/// Alternates between two parsers, merging the results (left associative) +/// +/// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see +/// [`cut_err`][crate::combinator::cut_err]. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::combinator::separated_foldl1; +/// use winnow::ascii::dec_int; +/// +/// fn parser(s: &str) -> IResult<&str, i32> { +/// separated_foldl1(dec_int, "-", |l, _, r| l - r).parse_peek(s) +/// } +/// +/// 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)))); +/// ``` +pub fn separated_foldl1<I, O, O2, E, P, S, Op>( + mut parser: P, + mut sep: S, + op: Op, +) -> impl Parser<I, O, E> +where + I: Stream, + P: Parser<I, O, E>, + S: Parser<I, O2, E>, + E: ParserError<I>, + Op: Fn(O, O2, O) -> O, +{ + trace("separated_foldl1", move |i: &mut I| { + let mut ol = parser.parse_next(i)?; + + loop { + let start = i.checkpoint(); + let len = i.eof_offset(); + match sep.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(ol); + } + Err(e) => return Err(e), + Ok(s) => { + // infinite loop check: the parser must always consume + if i.eof_offset() == len { + return Err(ErrMode::assert(i, "`repeat` parsers must always consume")); + } + + match parser.parse_next(i) { + Err(ErrMode::Backtrack(_)) => { + i.reset(start); + return Ok(ol); + } + Err(e) => return Err(e), + Ok(or) => { + ol = op(ol, s, or); + } + } + } + } + } + }) +} + +/// Alternates between two parsers, merging the results (right associative) +/// +/// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see +/// [`cut_err`][crate::combinator::cut_err]. +/// +/// # Example +/// +/// ``` +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::combinator::separated_foldr1; +/// use winnow::ascii::dec_uint; +/// +/// fn parser(s: &str) -> IResult<&str, u32> { +/// separated_foldr1(dec_uint, "^", |l: u32, _, r: u32| l.pow(r)).parse_peek(s) +/// } +/// +/// 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)))); +/// ``` +#[cfg(feature = "alloc")] +pub fn separated_foldr1<I, O, O2, E, P, S, Op>( + mut parser: P, + mut sep: S, + op: Op, +) -> impl Parser<I, O, E> +where + I: Stream, + P: Parser<I, O, E>, + S: Parser<I, O2, E>, + E: ParserError<I>, + Op: Fn(O, O2, O) -> O, +{ + trace("separated_foldr1", move |i: &mut I| { + let ol = parser.parse_next(i)?; + let all: crate::lib::std::vec::Vec<(O2, O)> = + repeat(0.., (sep.by_ref(), parser.by_ref())).parse_next(i)?; + if let Some((s, or)) = all + .into_iter() + .rev() + .reduce(|(sr, or), (sl, ol)| (sl, op(ol, sr, or))) + { + let merged = op(ol, s, or); + Ok(merged) + } else { + Ok(ol) + } + }) +} + +fn repeat_m_n_<I, O, C, E, F>(min: usize, max: usize, parse: &mut F, input: &mut I) -> PResult<C, E> +where + I: Stream, + C: Accumulate<O>, + F: Parser<I, O, E>, + E: ParserError<I>, +{ + if min > max { + return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many))); + } + + let mut res = C::initial(Some(min)); + 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", + )); + } + + res.accumulate(value); + } + Err(ErrMode::Backtrack(e)) => { + if count < min { + return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many))); + } else { + input.reset(start); + return Ok(res); + } + } + Err(e) => { + return Err(e); + } + } + } + + Ok(res) +} + +fn repeat_n_<I, O, C, E, F>(count: usize, f: &mut F, i: &mut I) -> PResult<C, E> +where + I: Stream, + C: Accumulate<O>, + F: Parser<I, O, E>, + E: ParserError<I>, +{ + let mut res = C::initial(Some(count)); + + for _ in 0..count { + match f.parse_next(i) { + Ok(o) => { + res.accumulate(o); + } + Err(e) => { + return Err(e.append(i, ErrorKind::Many)); + } + } + } + + Ok(res) +} + +/// Repeats the embedded parser, filling the given slice with results. +/// +/// 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 = ["", ""]; +/// let (rest, ()) = fill("abc", &mut buf).parse_peek(s)?; +/// Ok((rest, buf)) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", ["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); +/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag)))); +/// 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 +where + I: Stream + 'a, + F: Parser<I, O, E> + 'a, + E: ParserError<I> + 'a, +{ + trace("fill", move |i: &mut I| { + for elem in buf.iter_mut() { + match f.parse_next(i) { + Ok(o) => { + *elem = o; + } + Err(e) => { + return Err(e.append(i, ErrorKind::Many)); + } + } + } + + Ok(()) + }) +} + +/// Repeats the embedded parser `m..=n` times, calling `g` 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 +/// * `m` The minimum number of iterations. +/// * `n` The maximum number of iterations. +/// * `f` The parser to apply. +/// * `init` A function returning the initial value. +/// * `g` The function that combines a result of `f` with +/// the current accumulator. +/// +/// **Warning:** If the parser passed to `fold_repeat` accepts empty inputs +/// (like `alpha0` or `digit0`), `fold_repeat` will return an error, +/// to prevent going into an infinite loop. +/// +/// # Example +/// +/// Zero or more repetitions: +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::combinator::fold_repeat; +/// use winnow::token::tag; +/// +/// fn parser(s: &str) -> IResult<&str, Vec<&str>> { +/// fold_repeat( +/// 0.., +/// "abc", +/// Vec::new, +/// |mut acc: Vec<_>, item| { +/// acc.push(item); +/// acc +/// } +/// ).parse_peek(s) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); +/// assert_eq!(parser("123123"), Ok(("123123", vec![]))); +/// assert_eq!(parser(""), Ok(("", vec![]))); +/// ``` +/// +/// One or more repetitions: +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::combinator::fold_repeat; +/// use winnow::token::tag; +/// +/// fn parser(s: &str) -> IResult<&str, Vec<&str>> { +/// fold_repeat( +/// 1.., +/// "abc", +/// Vec::new, +/// |mut acc: Vec<_>, item| { +/// acc.push(item); +/// acc +/// } +/// ).parse_peek(s) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); +/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Many)))); +/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Many)))); +/// ``` +/// +/// Arbitrary number of repetitions: +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::combinator::fold_repeat; +/// use winnow::token::tag; +/// +/// fn parser(s: &str) -> IResult<&str, Vec<&str>> { +/// fold_repeat( +/// 0..=2, +/// "abc", +/// Vec::new, +/// |mut acc: Vec<_>, item| { +/// acc.push(item); +/// acc +/// } +/// ).parse_peek(s) +/// } +/// +/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); +/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); +/// assert_eq!(parser("123123"), Ok(("123123", vec![]))); +/// assert_eq!(parser(""), Ok(("", vec![]))); +/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); +/// ``` +#[doc(alias = "fold_many0")] +#[doc(alias = "fold_many1")] +#[doc(alias = "fold_many_m_n")] +#[inline(always)] +pub fn fold_repeat<I, O, E, F, G, H, R>( + range: impl Into<Range>, + mut f: F, + mut init: H, + mut 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>, +{ + let Range { + start_inclusive, + end_inclusive, + } = range.into(); + trace("fold_repeat", move |i: &mut I| { + match (start_inclusive, end_inclusive) { + (0, None) => fold_repeat0_(&mut f, &mut init, &mut g, i), + (1, None) => fold_repeat1_(&mut f, &mut init, &mut g, i), + (start, end) => fold_repeat_m_n_( + start, + end.unwrap_or(usize::MAX), + &mut f, + &mut init, + &mut g, + i, + ), + } + }) +} + +fn fold_repeat0_<I, O, E, F, G, H, R>( + f: &mut F, + init: &mut H, + g: &mut G, + input: &mut I, +) -> PResult<R, E> +where + I: Stream, + F: Parser<I, O, E>, + G: FnMut(R, O) -> R, + H: FnMut() -> R, + E: ParserError<I>, +{ + let mut res = init(); + + loop { + let start = input.checkpoint(); + let len = input.eof_offset(); + match f.parse_next(input) { + Ok(o) => { + // infinite loop check: the parser must always consume + if input.eof_offset() == len { + return Err(ErrMode::assert( + input, + "`repeat` parsers must always consume", + )); + } + + res = g(res, o); + } + Err(ErrMode::Backtrack(_)) => { + input.reset(start); + return Ok(res); + } + Err(e) => { + return Err(e); + } + } + } +} + +fn fold_repeat1_<I, O, E, F, G, H, R>( + f: &mut F, + init: &mut H, + g: &mut G, + input: &mut I, +) -> PResult<R, E> +where + I: Stream, + F: Parser<I, O, E>, + G: FnMut(R, O) -> R, + H: FnMut() -> R, + E: ParserError<I>, +{ + let init = init(); + match f.parse_next(input) { + Err(ErrMode::Backtrack(_)) => Err(ErrMode::from_error_kind(input, ErrorKind::Many)), + Err(e) => Err(e), + Ok(o1) => { + let mut acc = g(init, o1); + + loop { + let start = input.checkpoint(); + let len = input.eof_offset(); + match f.parse_next(input) { + Err(ErrMode::Backtrack(_)) => { + input.reset(start); + break; + } + Err(e) => return Err(e), + Ok(o) => { + // infinite loop check: the parser must always consume + if input.eof_offset() == len { + return Err(ErrMode::assert( + input, + "`repeat` parsers must always consume", + )); + } + + acc = g(acc, o); + } + } + } + + Ok(acc) + } + } +} + +fn fold_repeat_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) -> R, + H: FnMut() -> R, + E: ParserError<I>, +{ + if min > max { + return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many))); + } + + 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", + )); + } + + acc = fold(acc, value); + } + //FInputXMError: handle failure properly + Err(ErrMode::Backtrack(err)) => { + if count < min { + return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Many))); + } else { + input.reset(start); + break; + } + } + Err(e) => return Err(e), + } + } + + Ok(acc) +} diff --git a/vendor/winnow/src/combinator/parser.rs b/vendor/winnow/src/combinator/parser.rs new file mode 100644 index 0000000..c95f1f6 --- /dev/null +++ b/vendor/winnow/src/combinator/parser.rs @@ -0,0 +1,863 @@ +use crate::error::{AddContext, ErrMode, ErrorKind, FromExternalError, ParserError}; +use crate::lib::std::borrow::Borrow; +use crate::lib::std::ops::Range; +use crate::stream::StreamIsPartial; +use crate::stream::{Location, Stream}; +use crate::trace::trace; +use crate::trace::trace_result; +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: Fn(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: Fn(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: Fn(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: Fn(&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: Fn(&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: Fn(&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::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) + } +} diff --git a/vendor/winnow/src/combinator/sequence.rs b/vendor/winnow/src/combinator/sequence.rs new file mode 100644 index 0000000..5cfeb9c --- /dev/null +++ b/vendor/winnow/src/combinator/sequence.rs @@ -0,0 +1,164 @@ +use crate::error::ParserError; +use crate::stream::Stream; +use crate::trace::trace; +use crate::*; + +/// Sequence two parsers, only returning the output from the second. +/// +/// # Arguments +/// * `first` The opening parser. +/// * `second` The second parser to get object. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::combinator::preceded; +/// use winnow::token::tag; +/// +/// let mut parser = preceded("abc", "efg"); +/// +/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "efg"))); +/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "efg"))); +/// 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)))); +/// ``` +#[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> +where + I: Stream, + F: Parser<I, O1, E>, + G: Parser<I, O2, E>, +{ + trace("preceded", move |input: &mut I| { + let _ = first.parse_next(input)?; + second.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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::error::Needed::Size; +/// use winnow::combinator::terminated; +/// use winnow::token::tag; +/// +/// let mut parser = terminated("abc", "efg"); +/// +/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "abc"))); +/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "abc"))); +/// 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)))); +/// ``` +#[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> +where + I: Stream, + F: Parser<I, O1, E>, + G: Parser<I, O2, E>, +{ + trace("terminated", move |input: &mut I| { + let o1 = first.parse_next(input)?; + second.parse_next(input).map(|_| o1) + }) +} + +/// 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::error::Needed::Size; +/// # use winnow::prelude::*; +/// use winnow::combinator::separated_pair; +/// use winnow::token::tag; +/// +/// let mut parser = separated_pair("abc", "|", "efg"); +/// +/// assert_eq!(parser.parse_peek("abc|efg"), Ok(("", ("abc", "efg")))); +/// assert_eq!(parser.parse_peek("abc|efghij"), Ok(("hij", ("abc", "efg")))); +/// 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> +where + I: Stream, + F: Parser<I, O1, E>, + G: Parser<I, O2, E>, + H: Parser<I, O3, E>, +{ + trace("separated_pair", move |input: &mut I| { + let o1 = first.parse_next(input)?; + let _ = sep.parse_next(input)?; + second.parse_next(input).map(|o2| (o1, o2)) + }) +} + +/// 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::error::Needed::Size; +/// # use winnow::prelude::*; +/// use winnow::combinator::delimited; +/// use winnow::token::tag; +/// +/// let mut parser = delimited("(", "abc", ")"); +/// +/// assert_eq!(parser.parse_peek("(abc)"), Ok(("", "abc"))); +/// assert_eq!(parser.parse_peek("(abc)def"), Ok(("def", "abc"))); +/// 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)))); +/// ``` +#[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> +where + I: Stream, + F: Parser<I, O1, E>, + G: Parser<I, O2, E>, + H: Parser<I, O3, E>, +{ + trace("delimited", move |input: &mut I| { + let _ = first.parse_next(input)?; + let o2 = second.parse_next(input)?; + third.parse_next(input).map(|_| o2) + }) +} diff --git a/vendor/winnow/src/combinator/tests.rs b/vendor/winnow/src/combinator/tests.rs new file mode 100644 index 0000000..9d2b49d --- /dev/null +++ b/vendor/winnow/src/combinator/tests.rs @@ -0,0 +1,1292 @@ +use super::*; + +use crate::ascii::digit1 as digit; +use crate::binary::u16; +use crate::binary::u8; +use crate::binary::Endianness; +use crate::error::ErrMode; +use crate::error::ErrorKind; +use crate::error::InputError; +use crate::error::Needed; +use crate::error::ParserError; +use crate::stream::Stream; +use crate::token::take; +use crate::unpeek; +use crate::IResult; +use crate::PResult; +use crate::Parser; +use crate::Partial; + +#[cfg(feature = "alloc")] +use crate::lib::std::vec::Vec; + +macro_rules! assert_parse( + ($left: expr, $right: expr) => { + let res: $crate::IResult<_, _, InputError<_>> = $left; + assert_eq!(res, $right); + }; +); + +#[test] +fn eof_on_slices() { + let not_over: &[u8] = &b"Hello, world!"[..]; + let is_over: &[u8] = &b""[..]; + + let res_not_over = eof.parse_peek(not_over); + assert_parse!( + res_not_over, + Err(ErrMode::Backtrack(error_position!( + ¬_over, + ErrorKind::Eof + ))) + ); + + let res_over = eof.parse_peek(is_over); + assert_parse!(res_over, Ok((is_over, is_over))); +} + +#[test] +fn eof_on_strs() { + let not_over: &str = "Hello, world!"; + let is_over: &str = ""; + + let res_not_over = eof.parse_peek(not_over); + assert_parse!( + res_not_over, + Err(ErrMode::Backtrack(error_position!( + ¬_over, + ErrorKind::Eof + ))) + ); + + let res_over = eof.parse_peek(is_over); + 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 { + CustomError + } +} + +impl<I> ParserError<I> for CustomError { + fn from_error_kind(_: &I, _: ErrorKind) -> Self { + CustomError + } + + fn append(self, _: &I, _: ErrorKind) -> Self { + CustomError + } +} + +struct CustomError; +#[allow(dead_code)] +fn custom_error(input: &[u8]) -> IResult<&[u8], &[u8], CustomError> { + //fix_error!(input, CustomError<_>, alphanumeric) + crate::ascii::alphanumeric1.parse_peek(input) +} + +#[test] +fn test_parser_flat_map() { + let input: &[u8] = &[3, 100, 101, 102, 103, 104][..]; + assert_parse!( + u8.flat_map(take).parse_peek(input), + Ok((&[103, 104][..], &[100, 101, 102][..])) + ); +} + +#[allow(dead_code)] +fn test_closure_compiles_195(input: &[u8]) -> IResult<&[u8], ()> { + u8.flat_map(|num| repeat(num as usize, u16(Endianness::Big))) + .parse_peek(input) +} + +#[test] +fn test_parser_verify_map() { + let input: &[u8] = &[50][..]; + assert_parse!( + u8.verify_map(|u| if u < 20 { Some(u) } else { None }) + .parse_peek(input), + Err(ErrMode::Backtrack(InputError::new( + &[50][..], + ErrorKind::Verify + ))) + ); + assert_parse!( + u8.verify_map(|u| if u > 20 { Some(u) } else { None }) + .parse_peek(input), + Ok((&[][..], 50)) + ); +} + +#[test] +fn test_parser_map_parser() { + let input: &[u8] = &[100, 101, 102, 103, 104][..]; + assert_parse!( + take(4usize).and_then(take(2usize)).parse_peek(input), + Ok((&[104][..], &[100, 101][..])) + ); +} + +#[test] +#[cfg(feature = "std")] +fn test_parser_into() { + use crate::error::InputError; + use crate::token::take; + + let mut parser = take::<_, _, InputError<_>>(3u8).output_into(); + let result: IResult<&[u8], Vec<u8>> = parser.parse_peek(&b"abcdefg"[..]); + + assert_eq!(result, Ok((&b"defg"[..], vec![97, 98, 99]))); +} + +#[test] +fn opt_test() { + fn opt_abcd(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Option<&[u8]>> { + opt("abcd").parse_peek(i) + } + + let a = &b"abcdef"[..]; + let b = &b"bcdefg"[..]; + let c = &b"ab"[..]; + assert_eq!( + opt_abcd(Partial::new(a)), + Ok((Partial::new(&b"ef"[..]), Some(&b"abcd"[..]))) + ); + assert_eq!( + opt_abcd(Partial::new(b)), + Ok((Partial::new(&b"bcdefg"[..]), None)) + ); + assert_eq!( + opt_abcd(Partial::new(c)), + Err(ErrMode::Incomplete(Needed::new(2))) + ); +} + +#[test] +fn peek_test() { + fn peek_tag(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + peek("abcd").parse_peek(i) + } + + assert_eq!( + peek_tag(Partial::new(&b"abcdef"[..])), + Ok((Partial::new(&b"abcdef"[..]), &b"abcd"[..])) + ); + assert_eq!( + peek_tag(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + peek_tag(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); +} + +#[test] +fn not_test() { + fn not_aaa(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, ()> { + not("aaa").parse_peek(i) + } + + assert_eq!( + not_aaa(Partial::new(&b"aaa"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"aaa"[..]), + ErrorKind::Not + ))) + ); + assert_eq!( + not_aaa(Partial::new(&b"aa"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + not_aaa(Partial::new(&b"abcd"[..])), + Ok((Partial::new(&b"abcd"[..]), ())) + ); +} + +#[test] +fn test_parser_verify() { + use crate::token::take; + + fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take(5u8) + .verify(|slice: &[u8]| slice[0] == b'a') + .parse_peek(i) + } + assert_eq!( + test(Partial::new(&b"bcd"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + test(Partial::new(&b"bcdefg"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"bcdefg"[..]), + ErrorKind::Verify + ))) + ); + assert_eq!( + test(Partial::new(&b"abcdefg"[..])), + Ok((Partial::new(&b"fg"[..]), &b"abcde"[..])) + ); +} + +#[test] +#[allow(unused)] +fn test_parser_verify_ref() { + use crate::token::take; + + let mut parser1 = take(3u8).verify(|s: &[u8]| s == &b"abc"[..]); + + assert_eq!( + parser1.parse_peek(&b"abcd"[..]), + Ok((&b"d"[..], &b"abc"[..])) + ); + assert_eq!( + parser1.parse_peek(&b"defg"[..]), + Err(ErrMode::Backtrack(InputError::new( + &b"defg"[..], + ErrorKind::Verify + ))) + ); + + fn parser2(i: &[u8]) -> IResult<&[u8], u32> { + crate::binary::be_u32 + .verify(|val: &u32| *val < 3) + .parse_peek(i) + } +} + +#[test] +#[cfg(feature = "alloc")] +fn test_parser_verify_alloc() { + use crate::token::take; + let mut parser1 = take(3u8) + .map(|s: &[u8]| s.to_vec()) + .verify(|s: &[u8]| s == &b"abc"[..]); + + assert_eq!( + parser1.parse_peek(&b"abcd"[..]), + Ok((&b"d"[..], b"abc".to_vec())) + ); + assert_eq!( + parser1.parse_peek(&b"defg"[..]), + Err(ErrMode::Backtrack(InputError::new( + &b"defg"[..], + ErrorKind::Verify + ))) + ); +} + +#[test] +fn fail_test() { + let a = "string"; + let b = "another string"; + + assert_eq!( + fail::<_, &str, _>.parse_peek(a), + Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Fail))) + ); + assert_eq!( + fail::<_, &str, _>.parse_peek(b), + Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Fail))) + ); +} + +#[test] +fn complete() { + fn err_test(i: &[u8]) -> IResult<&[u8], &[u8]> { + let (i, _) = "ijkl".parse_peek(i)?; + "mnop".parse_peek(i) + } + let a = &b"ijklmn"[..]; + + let res_a = err_test(a); + assert_eq!( + res_a, + Err(ErrMode::Backtrack(error_position!( + &&b"mn"[..], + ErrorKind::Tag + ))) + ); +} + +#[test] +fn separated_pair_test() { + #[allow(clippy::type_complexity)] + fn sep_pair_abc_def(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8])> { + separated_pair("abc", ",", "def").parse_peek(i) + } + + assert_eq!( + sep_pair_abc_def(Partial::new(&b"abc,defghijkl"[..])), + Ok((Partial::new(&b"ghijkl"[..]), (&b"abc"[..], &b"def"[..]))) + ); + assert_eq!( + sep_pair_abc_def(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + sep_pair_abc_def(Partial::new(&b"abc,d"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + sep_pair_abc_def(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + sep_pair_abc_def(Partial::new(&b"xxx,def"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx,def"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + sep_pair_abc_def(Partial::new(&b"abc,xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); +} + +#[test] +fn preceded_test() { + fn preceded_abcd_efgh(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + preceded("abcd", "efgh").parse_peek(i) + } + + assert_eq!( + preceded_abcd_efgh(Partial::new(&b"abcdefghijkl"[..])), + Ok((Partial::new(&b"ijkl"[..]), &b"efgh"[..])) + ); + assert_eq!( + preceded_abcd_efgh(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + preceded_abcd_efgh(Partial::new(&b"abcde"[..])), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_eq!( + preceded_abcd_efgh(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + preceded_abcd_efgh(Partial::new(&b"xxxxdef"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxxdef"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + preceded_abcd_efgh(Partial::new(&b"abcdxxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); +} + +#[test] +fn terminated_test() { + fn terminated_abcd_efgh(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + terminated("abcd", "efgh").parse_peek(i) + } + + assert_eq!( + terminated_abcd_efgh(Partial::new(&b"abcdefghijkl"[..])), + Ok((Partial::new(&b"ijkl"[..]), &b"abcd"[..])) + ); + assert_eq!( + terminated_abcd_efgh(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + terminated_abcd_efgh(Partial::new(&b"abcde"[..])), + Err(ErrMode::Incomplete(Needed::new(3))) + ); + assert_eq!( + terminated_abcd_efgh(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + terminated_abcd_efgh(Partial::new(&b"xxxxdef"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxxdef"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + terminated_abcd_efgh(Partial::new(&b"abcdxxxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxx"[..]), + ErrorKind::Tag + ))) + ); +} + +#[test] +fn delimited_test() { + fn delimited_abc_def_ghi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + delimited("abc", "def", "ghi").parse_peek(i) + } + + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"abcdefghijkl"[..])), + Ok((Partial::new(&b"jkl"[..]), &b"def"[..])) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"abcde"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"abcdefgh"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"xxxdefghi"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxdefghi"[..]), + ErrorKind::Tag + ),)) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"abcxxxghi"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxghi"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + delimited_abc_def_ghi(Partial::new(&b"abcdefxxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn alt_test() { + #[cfg(feature = "alloc")] + use crate::{ + error::ParserError, + lib::std::{ + fmt::Debug, + string::{String, ToString}, + }, + }; + + #[cfg(feature = "alloc")] + #[derive(Debug, Clone, Eq, PartialEq)] + pub struct ErrorStr(String); + + #[cfg(feature = "alloc")] + impl From<u32> for ErrorStr { + fn from(i: u32) -> Self { + 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)) + } + } + + #[cfg(feature = "alloc")] + impl<I: Debug> ParserError<I> for ErrorStr { + fn from_error_kind(input: &I, kind: ErrorKind) -> Self { + ErrorStr(format!("custom error message: ({:?}, {:?})", input, kind)) + } + + fn append(self, input: &I, kind: ErrorKind) -> Self { + ErrorStr(format!( + "custom error message: ({:?}, {:?}) - {:?}", + input, kind, self + )) + } + } + + fn work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { + Ok(input.peek_finish()) + } + + #[allow(unused_variables)] + fn dont_work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { + Err(ErrMode::Backtrack(ErrorStr("abcd".to_string()))) + } + + fn work2(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { + Ok((input, &b""[..])) + } + + fn alt1(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { + alt((unpeek(dont_work), unpeek(dont_work))).parse_peek(i) + } + fn alt2(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { + alt((unpeek(dont_work), unpeek(work))).parse_peek(i) + } + fn alt3(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { + alt(( + unpeek(dont_work), + unpeek(dont_work), + unpeek(work2), + unpeek(dont_work), + )) + .parse_peek(i) + } + //named!(alt1, alt!(dont_work | dont_work)); + //named!(alt2, alt!(dont_work | work)); + //named!(alt3, alt!(dont_work | dont_work | work2 | dont_work)); + + let a = &b"abcd"[..]; + assert_eq!( + alt1(a), + Err(ErrMode::Backtrack(error_node_position!( + &a, + ErrorKind::Alt, + ErrorStr("abcd".to_string()) + ))) + ); + assert_eq!(alt2(a), Ok((&b""[..], a))); + assert_eq!(alt3(a), Ok((a, &b""[..]))); + + fn alt4(i: &[u8]) -> IResult<&[u8], &[u8]> { + alt(("abcd", "efgh")).parse_peek(i) + } + let b = &b"efgh"[..]; + assert_eq!(alt4(a), Ok((&b""[..], a))); + assert_eq!(alt4(b), Ok((&b""[..], b))); +} + +#[test] +fn alt_incomplete() { + fn alt1(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + alt(("a", "bc", "def")).parse_peek(i) + } + + let a = &b""[..]; + assert_eq!( + alt1(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + let a = &b"b"[..]; + assert_eq!( + alt1(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + let a = &b"bcd"[..]; + assert_eq!( + alt1(Partial::new(a)), + Ok((Partial::new(&b"d"[..]), &b"bc"[..])) + ); + let a = &b"cde"[..]; + assert_eq!( + alt1(Partial::new(a)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(a), + ErrorKind::Tag + ))) + ); + let a = &b"de"[..]; + assert_eq!( + alt1(Partial::new(a)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + let a = &b"defg"[..]; + assert_eq!( + alt1(Partial::new(a)), + Ok((Partial::new(&b"g"[..]), &b"def"[..])) + ); +} + +#[test] +fn alt_array() { + fn alt1<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8]> { + alt(["a", "bc", "def"]).parse_next(i) + } + + let i = &b"a"[..]; + assert_eq!(alt1.parse_peek(i), Ok((&b""[..], (&b"a"[..])))); + + let i = &b"bc"[..]; + assert_eq!(alt1.parse_peek(i), Ok((&b""[..], (&b"bc"[..])))); + + let i = &b"defg"[..]; + assert_eq!(alt1.parse_peek(i), Ok((&b"g"[..], (&b"def"[..])))); + + let i = &b"z"[..]; + assert_eq!( + alt1.parse_peek(i), + Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Tag))) + ); +} + +#[test] +fn permutation_test() { + #[allow(clippy::type_complexity)] + fn perm(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8], &[u8])> { + permutation(("abcd", "efg", "hi")).parse_peek(i) + } + + let expected = (&b"abcd"[..], &b"efg"[..], &b"hi"[..]); + + let a = &b"abcdefghijk"[..]; + assert_eq!( + perm(Partial::new(a)), + Ok((Partial::new(&b"jk"[..]), expected)) + ); + let b = &b"efgabcdhijk"[..]; + assert_eq!( + perm(Partial::new(b)), + Ok((Partial::new(&b"jk"[..]), expected)) + ); + let c = &b"hiefgabcdjk"[..]; + assert_eq!( + perm(Partial::new(c)), + Ok((Partial::new(&b"jk"[..]), expected)) + ); + + let d = &b"efgxyzabcdefghi"[..]; + assert_eq!( + perm(Partial::new(d)), + Err(ErrMode::Backtrack(error_node_position!( + &Partial::new(&b"efgxyzabcdefghi"[..]), + ErrorKind::Alt, + error_position!(&Partial::new(&b"xyzabcdefghi"[..]), ErrorKind::Tag) + ))) + ); + + let e = &b"efgabc"[..]; + assert_eq!( + perm(Partial::new(e)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn separated0_test() { + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + separated0("abcd", ",").parse_peek(i) + } + fn multi_empty(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + separated0("", ",").parse_peek(i) + } + fn multi_longsep(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + separated0("abcd", "..").parse_peek(i) + } + + let a = &b"abcdef"[..]; + let b = &b"abcd,abcdef"[..]; + let c = &b"azerty"[..]; + let d = &b",,abc"[..]; + let e = &b"abcd,abcd,ef"[..]; + let f = &b"abc"[..]; + let g = &b"abcd."[..]; + let h = &b"abcd,abc"[..]; + + let res1 = vec![&b"abcd"[..]]; + assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); + let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; + assert_eq!(multi(Partial::new(b)), Ok((Partial::new(&b"ef"[..]), res2))); + assert_eq!( + multi(Partial::new(c)), + Ok((Partial::new(&b"azerty"[..]), Vec::new())) + ); + let res3 = vec![&b""[..], &b""[..], &b""[..]]; + assert_eq!( + multi_empty(Partial::new(d)), + Ok((Partial::new(&b"abc"[..]), res3)) + ); + let res4 = vec![&b"abcd"[..], &b"abcd"[..]]; + assert_eq!( + multi(Partial::new(e)), + Ok((Partial::new(&b",ef"[..]), res4)) + ); + + assert_eq!( + multi(Partial::new(f)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + multi_longsep(Partial::new(g)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + multi(Partial::new(h)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +#[cfg_attr(debug_assertions, should_panic)] +fn separated0_empty_sep_test() { + fn empty_sep(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + separated0("abc", "").parse_peek(i) + } + + let i = &b"abcabc"[..]; + + let i_err_pos = &i[3..]; + assert_eq!( + empty_sep(Partial::new(i)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(i_err_pos), + ErrorKind::Assert + ))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn separated1_test() { + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + separated1("abcd", ",").parse_peek(i) + } + fn multi_longsep(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + separated1("abcd", "..").parse_peek(i) + } + + let a = &b"abcdef"[..]; + let b = &b"abcd,abcdef"[..]; + let c = &b"azerty"[..]; + let d = &b"abcd,abcd,ef"[..]; + + let f = &b"abc"[..]; + let g = &b"abcd."[..]; + let h = &b"abcd,abc"[..]; + + let res1 = vec![&b"abcd"[..]]; + assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); + let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; + assert_eq!(multi(Partial::new(b)), Ok((Partial::new(&b"ef"[..]), res2))); + assert_eq!( + multi(Partial::new(c)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(c), + ErrorKind::Tag + ))) + ); + let res3 = vec![&b"abcd"[..], &b"abcd"[..]]; + assert_eq!( + multi(Partial::new(d)), + Ok((Partial::new(&b",ef"[..]), res3)) + ); + + assert_eq!( + multi(Partial::new(f)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + multi_longsep(Partial::new(g)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + multi(Partial::new(h)), + Err(ErrMode::Incomplete(Needed::new(1))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn repeat0_test() { + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + repeat(0.., "abcd").parse_peek(i) + } + + assert_eq!( + multi(Partial::new(&b"abcdef"[..])), + Ok((Partial::new(&b"ef"[..]), vec![&b"abcd"[..]])) + ); + assert_eq!( + multi(Partial::new(&b"abcdabcdefgh"[..])), + Ok((Partial::new(&b"efgh"[..]), vec![&b"abcd"[..], &b"abcd"[..]])) + ); + assert_eq!( + multi(Partial::new(&b"azerty"[..])), + Ok((Partial::new(&b"azerty"[..]), Vec::new())) + ); + assert_eq!( + multi(Partial::new(&b"abcdab"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + multi(Partial::new(&b"abcd"[..])), + Err(ErrMode::Incomplete(Needed::new(4))) + ); + assert_eq!( + multi(Partial::new(&b""[..])), + Err(ErrMode::Incomplete(Needed::new(4))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +#[cfg_attr(debug_assertions, should_panic)] +fn repeat0_empty_test() { + fn multi_empty(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + repeat(0.., "").parse_peek(i) + } + + assert_eq!( + multi_empty(Partial::new(&b"abcdef"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"abcdef"[..]), + ErrorKind::Assert + ))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn repeat1_test() { + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + repeat(1.., "abcd").parse_peek(i) + } + + let a = &b"abcdef"[..]; + let b = &b"abcdabcdefgh"[..]; + let c = &b"azerty"[..]; + let d = &b"abcdab"[..]; + + let res1 = vec![&b"abcd"[..]]; + assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); + let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; + assert_eq!( + multi(Partial::new(b)), + Ok((Partial::new(&b"efgh"[..]), res2)) + ); + assert_eq!( + multi(Partial::new(c)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(c), + ErrorKind::Tag + ))) + ); + assert_eq!( + multi(Partial::new(d)), + Err(ErrMode::Incomplete(Needed::new(2))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn repeat_till_test() { + #[allow(clippy::type_complexity)] + fn multi(i: &[u8]) -> IResult<&[u8], (Vec<&[u8]>, &[u8])> { + repeat_till0("abcd", "efgh").parse_peek(i) + } + + let a = b"abcdabcdefghabcd"; + let b = b"efghabcd"; + let c = b"azerty"; + + let res_a = (vec![&b"abcd"[..], &b"abcd"[..]], &b"efgh"[..]); + let res_b: (Vec<&[u8]>, &[u8]) = (Vec::new(), &b"efgh"[..]); + assert_eq!(multi(&a[..]), Ok((&b"abcd"[..], res_a))); + assert_eq!(multi(&b[..]), Ok((&b"abcd"[..], res_b))); + assert_eq!( + multi(&c[..]), + Err(ErrMode::Backtrack(error_node_position!( + &&c[..], + ErrorKind::Many, + error_position!(&&c[..], ErrorKind::Tag) + ))) + ); +} + +#[test] +#[cfg(feature = "std")] +fn infinite_many() { + fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> { + println!("input: {:?}", input); + Err(ErrMode::Backtrack(error_position!(&input, ErrorKind::Tag))) + } + + // should not go into an infinite loop + fn multi0(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + repeat(0.., unpeek(tst)).parse_peek(i) + } + let a = &b"abcdef"[..]; + assert_eq!(multi0(a), Ok((a, Vec::new()))); + + fn multi1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + repeat(1.., unpeek(tst)).parse_peek(i) + } + let a = &b"abcdef"[..]; + assert_eq!( + multi1(a), + Err(ErrMode::Backtrack(error_position!(&a, ErrorKind::Tag))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn repeat_test() { + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + repeat(2..=4, "Abcd").parse_peek(i) + } + + let a = &b"Abcdef"[..]; + let b = &b"AbcdAbcdefgh"[..]; + let c = &b"AbcdAbcdAbcdAbcdefgh"[..]; + let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..]; + let e = &b"AbcdAb"[..]; + + assert_eq!( + multi(Partial::new(a)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"ef"[..]), + ErrorKind::Tag + ))) + ); + let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]]; + assert_eq!( + multi(Partial::new(b)), + Ok((Partial::new(&b"efgh"[..]), res1)) + ); + let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]]; + assert_eq!( + multi(Partial::new(c)), + Ok((Partial::new(&b"efgh"[..]), res2)) + ); + let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]]; + assert_eq!( + multi(Partial::new(d)), + Ok((Partial::new(&b"Abcdefgh"[..]), res3)) + ); + assert_eq!( + multi(Partial::new(e)), + Err(ErrMode::Incomplete(Needed::new(2))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn count_test() { + const TIMES: usize = 2; + fn cnt_2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + repeat(TIMES, "abc").parse_peek(i) + } + + assert_eq!( + cnt_2(Partial::new(&b"abcabcabcdef"[..])), + Ok((Partial::new(&b"abcdef"[..]), vec![&b"abc"[..], &b"abc"[..]])) + ); + assert_eq!( + cnt_2(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + cnt_2(Partial::new(&b"abcab"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + cnt_2(Partial::new(&b"xxx"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxx"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + cnt_2(Partial::new(&b"xxxabcabcdef"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxabcabcdef"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + cnt_2(Partial::new(&b"abcxxxabcdef"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"xxxabcdef"[..]), + ErrorKind::Tag + ))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn count_zero() { + const TIMES: usize = 0; + fn counter_2(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { + repeat(TIMES, "abc").parse_peek(i) + } + + let done = &b"abcabcabcdef"[..]; + let parsed_done = Vec::new(); + let rest = done; + let incomplete_1 = &b"ab"[..]; + let parsed_incompl_1 = Vec::new(); + let incomplete_2 = &b"abcab"[..]; + let parsed_incompl_2 = Vec::new(); + let error = &b"xxx"[..]; + let error_remain = &b"xxx"[..]; + let parsed_err = Vec::new(); + let error_1 = &b"xxxabcabcdef"[..]; + let parsed_err_1 = Vec::new(); + let error_1_remain = &b"xxxabcabcdef"[..]; + let error_2 = &b"abcxxxabcdef"[..]; + let parsed_err_2 = Vec::new(); + let error_2_remain = &b"abcxxxabcdef"[..]; + + assert_eq!(counter_2(done), Ok((rest, parsed_done))); + assert_eq!( + counter_2(incomplete_1), + Ok((incomplete_1, parsed_incompl_1)) + ); + assert_eq!( + counter_2(incomplete_2), + Ok((incomplete_2, parsed_incompl_2)) + ); + assert_eq!(counter_2(error), Ok((error_remain, parsed_err))); + assert_eq!(counter_2(error_1), Ok((error_1_remain, parsed_err_1))); + assert_eq!(counter_2(error_2), Ok((error_2_remain, parsed_err_2))); +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct NilError; + +impl<I> From<(I, ErrorKind)> for NilError { + fn from(_: (I, ErrorKind)) -> Self { + NilError + } +} + +impl<I> ParserError<I> for NilError { + fn from_error_kind(_: &I, _: ErrorKind) -> NilError { + NilError + } + fn append(self, _: &I, _: ErrorKind) -> NilError { + NilError + } +} + +#[test] +#[cfg(feature = "alloc")] +fn fold_repeat0_test() { + fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> { + acc.push(item); + acc + } + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + fold_repeat(0.., "abcd", Vec::new, fold_into_vec).parse_peek(i) + } + + assert_eq!( + multi(Partial::new(&b"abcdef"[..])), + Ok((Partial::new(&b"ef"[..]), vec![&b"abcd"[..]])) + ); + assert_eq!( + multi(Partial::new(&b"abcdabcdefgh"[..])), + Ok((Partial::new(&b"efgh"[..]), vec![&b"abcd"[..], &b"abcd"[..]])) + ); + assert_eq!( + multi(Partial::new(&b"azerty"[..])), + Ok((Partial::new(&b"azerty"[..]), Vec::new())) + ); + assert_eq!( + multi(Partial::new(&b"abcdab"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + multi(Partial::new(&b"abcd"[..])), + Err(ErrMode::Incomplete(Needed::new(4))) + ); + assert_eq!( + multi(Partial::new(&b""[..])), + Err(ErrMode::Incomplete(Needed::new(4))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +#[cfg_attr(debug_assertions, should_panic)] +fn fold_repeat0_empty_test() { + fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> { + acc.push(item); + acc + } + fn multi_empty(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + fold_repeat(0.., "", Vec::new, fold_into_vec).parse_peek(i) + } + + assert_eq!( + multi_empty(Partial::new(&b"abcdef"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"abcdef"[..]), + ErrorKind::Assert + ))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn fold_repeat1_test() { + fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> { + acc.push(item); + acc + } + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + fold_repeat(1.., "abcd", Vec::new, fold_into_vec).parse_peek(i) + } + + let a = &b"abcdef"[..]; + let b = &b"abcdabcdefgh"[..]; + let c = &b"azerty"[..]; + let d = &b"abcdab"[..]; + + let res1 = vec![&b"abcd"[..]]; + assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); + let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; + assert_eq!( + multi(Partial::new(b)), + Ok((Partial::new(&b"efgh"[..]), res2)) + ); + assert_eq!( + multi(Partial::new(c)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(c), + ErrorKind::Many + ))) + ); + assert_eq!( + multi(Partial::new(d)), + Err(ErrMode::Incomplete(Needed::new(2))) + ); +} + +#[test] +#[cfg(feature = "alloc")] +fn fold_repeat_test() { + fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> { + acc.push(item); + acc + } + fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> { + fold_repeat(2..=4, "Abcd", Vec::new, fold_into_vec).parse_peek(i) + } + + let a = &b"Abcdef"[..]; + let b = &b"AbcdAbcdefgh"[..]; + let c = &b"AbcdAbcdAbcdAbcdefgh"[..]; + let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..]; + let e = &b"AbcdAb"[..]; + + assert_eq!( + multi(Partial::new(a)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"ef"[..]), + ErrorKind::Tag + ))) + ); + let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]]; + assert_eq!( + multi(Partial::new(b)), + Ok((Partial::new(&b"efgh"[..]), res1)) + ); + let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]]; + assert_eq!( + multi(Partial::new(c)), + Ok((Partial::new(&b"efgh"[..]), res2)) + ); + let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]]; + assert_eq!( + multi(Partial::new(d)), + Ok((Partial::new(&b"Abcdefgh"[..]), res3)) + ); + assert_eq!( + multi(Partial::new(e)), + Err(ErrMode::Incomplete(Needed::new(2))) + ); +} + +#[test] +fn repeat0_count_test() { + fn count0_nums(i: &[u8]) -> IResult<&[u8], usize> { + repeat(0.., (digit, ",")).parse_peek(i) + } + + assert_eq!(count0_nums(&b"123,junk"[..]), Ok((&b"junk"[..], 1))); + + assert_eq!(count0_nums(&b"123,45,junk"[..]), Ok((&b"junk"[..], 2))); + + assert_eq!( + count0_nums(&b"1,2,3,4,5,6,7,8,9,0,junk"[..]), + Ok((&b"junk"[..], 10)) + ); + + assert_eq!(count0_nums(&b"hello"[..]), Ok((&b"hello"[..], 0))); +} + +#[test] +fn repeat1_count_test() { + fn count1_nums(i: &[u8]) -> IResult<&[u8], usize> { + repeat(1.., (digit, ",")).parse_peek(i) + } + + assert_eq!(count1_nums(&b"123,45,junk"[..]), Ok((&b"junk"[..], 2))); + + assert_eq!( + count1_nums(&b"1,2,3,4,5,6,7,8,9,0,junk"[..]), + Ok((&b"junk"[..], 10)) + ); + + assert_eq!( + count1_nums(&b"hello"[..]), + Err(ErrMode::Backtrack(error_position!( + &&b"hello"[..], + ErrorKind::Slice + ))) + ); +} diff --git a/vendor/winnow/src/error.rs b/vendor/winnow/src/error.rs new file mode 100644 index 0000000..0f08351 --- /dev/null +++ b/vendor/winnow/src/error.rs @@ -0,0 +1,1387 @@ +//! # Error management +//! +//! Errors are designed with multiple needs in mind: +//! - Accumulate more [context][Parser::context] as the error goes up the parser chain +//! - Distinguish between [recoverable errors, +//! unrecoverable errors, and more data is needed][ErrMode] +//! - Have a very low overhead, as errors are often discarded by the calling parser (examples: `many0`, `alt`) +//! - Can be modified according to the user's needs, because some languages need a lot more information +//! - Help thread-through the [stream][crate::stream] +//! +//! To abstract these needs away from the user, generally `winnow` parsers use the [`PResult`] +//! alias, rather than [`Result`]. [`Parser::parse`] is a top-level operation +//! that can help convert to a `Result` for integrating with your application's error reporting. +//! +//! Error types include: +//! - `()` +//! - [`ErrorKind`] +//! - [`InputError`] (mostly for testing) +//! - [`ContextError`] +//! - [`TreeError`] (mostly for testing) +//! - [Custom errors][crate::_topic::error] + +#[cfg(feature = "alloc")] +use crate::lib::std::borrow::ToOwned; +use crate::lib::std::fmt; +use core::num::NonZeroUsize; + +use crate::stream::AsBStr; +use crate::stream::Stream; +#[allow(unused_imports)] // Here for intra-doc links +use crate::Parser; + +/// For use with [`Parser::parse_peek`] which allows the input stream to be threaded through a +/// parser. +/// +/// - `Ok((I, O))` is the remaining [input][crate::stream] and the parsed value +/// - [`Err(ErrMode<E>)`][ErrMode] is the error along with how to respond to it +/// +/// By default, the error type (`E`) is [`InputError`] +/// +/// When integrating into the result of the application, see +/// - [`Parser::parse`] +/// - [`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` +/// +/// **Note:** This is only possible for `Stream` that are [partial][`crate::stream::StreamIsPartial`], +/// like [`Partial`][crate::Partial]. +#[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 + Size(NonZeroUsize), +} + +impl Needed { + /// Creates `Needed` instance, returns `Needed::Unknown` if the argument is zero + pub fn new(s: usize) -> Self { + match NonZeroUsize::new(s) { + Some(sz) => Needed::Size(sz), + None => Needed::Unknown, + } + } + + /// Indicates if we know how many bytes we need + pub fn is_known(&self) -> bool { + *self != Needed::Unknown + } + + /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value. + #[inline] + pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed { + match self { + Needed::Unknown => Needed::Unknown, + Needed::Size(n) => Needed::new(f(n)), + } + } +} + +/// 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 + /// + /// More data needs to be buffered before retrying the parse. + /// + /// This must only be set when the [`Stream`] is [partial][`crate::stream::StreamIsPartial`], like with + /// [`Partial`][crate::Partial] + /// + /// Convert this into an `Backtrack` with [`Parser::complete_err`] + Incomplete(Needed), + /// The parser failed with a recoverable error (the default). + /// + /// For example, a parser for json values might include a + /// [`dec_uint`][crate::ascii::dec_uint] as one case in an [`alt`][crate::combinator::alt] + /// combiantor. If it fails, the next case should be tried. + Backtrack(E), + /// The parser had an unrecoverable error. + /// + /// The parser was on the right branch, so directly report it to the user rather than trying + /// other branches. You can use [`cut_err()`][crate::combinator::cut_err] combinator to switch + /// from `ErrMode::Backtrack` to `ErrMode::Cut`. + /// + /// For example, one case in an [`alt`][crate::combinator::alt] combinator found a unique prefix + /// and you want any further errors parsing the case to be reported to the user. + Cut(E), +} + +impl<E> ErrMode<E> { + /// Tests if the result is Incomplete + #[inline] + pub fn is_incomplete(&self) -> bool { + matches!(self, ErrMode::Incomplete(_)) + } + + /// Prevent backtracking, bubbling the error up to the top + pub fn cut(self) -> Self { + match self { + ErrMode::Backtrack(e) => ErrMode::Cut(e), + rest => rest, + } + } + + /// Enable backtracking support + pub fn backtrack(self) -> Self { + match self { + ErrMode::Cut(e) => ErrMode::Backtrack(e), + rest => rest, + } + } + + /// Applies the given function to the inner error + pub fn map<E2, F>(self, f: F) -> ErrMode<E2> + where + F: FnOnce(E) -> E2, + { + match self { + ErrMode::Incomplete(n) => ErrMode::Incomplete(n), + ErrMode::Cut(t) => ErrMode::Cut(f(t)), + ErrMode::Backtrack(t) => ErrMode::Backtrack(f(t)), + } + } + + /// Automatically converts between errors if the underlying type supports it + pub fn convert<F>(self) -> ErrMode<F> + where + E: ErrorConvert<F>, + { + self.map(ErrorConvert::convert) + } + + /// 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 { + ErrMode::Backtrack(e) | ErrMode::Cut(e) => Some(e), + ErrMode::Incomplete(_) => None, + } + } +} + +impl<I, 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)) + } + + #[cfg_attr(debug_assertions, track_caller)] + #[inline(always)] + fn assert(input: &I, message: &'static str) -> Self + where + I: crate::lib::std::fmt::Debug, + { + ErrMode::Backtrack(E::assert(input, message)) + } + + #[inline] + fn append(self, input: &I, kind: ErrorKind) -> Self { + match self { + ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, kind)), + e => e, + } + } + + fn or(self, other: Self) -> Self { + match (self, other) { + (ErrMode::Backtrack(e), ErrMode::Backtrack(o)) => ErrMode::Backtrack(e.or(o)), + (ErrMode::Incomplete(e), _) | (_, ErrMode::Incomplete(e)) => ErrMode::Incomplete(e), + (ErrMode::Cut(e), _) | (_, ErrMode::Cut(e)) => ErrMode::Cut(e), + } + } +} + +impl<I, EXT, E> FromExternalError<I, EXT> for ErrMode<E> +where + E: FromExternalError<I, EXT>, +{ + #[inline(always)] + fn from_external_error(input: &I, kind: ErrorKind, e: EXT) -> Self { + ErrMode::Backtrack(E::from_external_error(input, kind, e)) + } +} + +impl<I, 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)) + } +} + +impl<T: Clone> ErrMode<InputError<T>> { + /// Maps `ErrMode<InputError<T>>` to `ErrMode<InputError<U>>` with the given `F: T -> U` + pub fn map_input<U: Clone, F>(self, f: F) -> ErrMode<InputError<U>> + where + F: FnOnce(T) -> U, + { + match self { + ErrMode::Incomplete(n) => ErrMode::Incomplete(n), + ErrMode::Cut(InputError { input, kind }) => ErrMode::Cut(InputError { + input: f(input), + kind, + }), + ErrMode::Backtrack(InputError { input, kind }) => ErrMode::Backtrack(InputError { + input: f(input), + kind, + }), + } + } +} + +impl<E: Eq> Eq for ErrMode<E> {} + +impl<E> fmt::Display for ErrMode<E> +where + E: fmt::Debug, +{ + 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::Unknown) => write!(f, "Parsing requires more data"), + ErrMode::Cut(c) => write!(f, "Parsing Failure: {:?}", c), + ErrMode::Backtrack(c) => write!(f, "Parsing Error: {:?}", c), + } + } +} + +/// The basic [`Parser`] trait for errors +/// +/// It provides methods to create an error from some combinators, +/// and combine existing errors in combinators like `alt`. +pub trait ParserError<I>: 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)] + fn assert(input: &I, _message: &'static str) -> Self + where + I: crate::lib::std::fmt::Debug, + { + #[cfg(debug_assertions)] + panic!("assert `{}` failed at {:#?}", _message, input); + #[cfg(not(debug_assertions))] + Self::from_error_kind(input, ErrorKind::Assert) + } + + /// Like [`ParserError::from_error_kind`] but merges it with the existing error. + /// + /// This is useful when backtracking through a parse tree, accumulating error context on the + /// way. + fn append(self, input: &I, kind: ErrorKind) -> Self; + + /// Combines errors from two different parse branches. + /// + /// For example, this would be used by [`alt`][crate::combinator::alt] to report the error from + /// each case. + #[inline] + fn or(self, other: Self) -> Self { + other + } +} + +/// 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 { + /// 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 { + self + } +} + +/// Create a new error with an external error, from [`std::str::FromStr`] +/// +/// This trait is required by the [`Parser::try_map`] combinator. +pub trait FromExternalError<I, E> { + /// Like [`ParserError::from_error_kind`] but also include an external error. + fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self; +} + +/// Equivalent of `From` implementation to avoid orphan rules in bits parsers +pub trait ErrorConvert<E> { + /// Transform to another error type + fn convert(self) -> E; +} + +/// Capture input on error +/// +/// This is useful for testing of generic parsers to ensure the error happens at the right +/// location. +/// +/// **Note:** [context][Parser::context] and inner errors (like from [`Parser::try_map`]) will be +/// dropped. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct InputError<I: Clone> { + /// The input stream, pointing to the location where the error occurred + pub input: I, + /// A rudimentary error kind + pub kind: ErrorKind, +} + +impl<I: Clone> InputError<I> { + /// Creates a new basic error + #[inline] + pub fn new(input: I, kind: ErrorKind) -> Self { + Self { input, kind } + } + + /// Translate the input type + #[inline] + pub fn map_input<I2: Clone, O: Fn(I) -> I2>(self, op: O) -> InputError<I2> { + InputError { + input: op(self.input), + kind: self.kind, + } + } +} + +#[cfg(feature = "alloc")] +impl<'i, I: ToOwned> InputError<&'i I> +where + <I as ToOwned>::Owned: Clone, +{ + /// Obtaining ownership + pub fn into_owned(self) -> InputError<<I as ToOwned>::Owned> { + self.map_input(ToOwned::to_owned) + } +} + +impl<I: Clone> ParserError<I> for InputError<I> { + #[inline] + fn from_error_kind(input: &I, kind: ErrorKind) -> Self { + Self { + input: input.clone(), + kind, + } + } + + #[inline] + fn append(self, _: &I, _: ErrorKind) -> Self { + self + } +} + +impl<I: Clone, C> AddContext<I, C> for InputError<I> {} + +impl<I: Clone, E> FromExternalError<I, E> for InputError<I> { + /// Create a new error from an input position and an external error + #[inline] + fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self { + Self { + input: input.clone(), + kind, + } + } +} + +impl<I: Clone> ErrorConvert<InputError<(I, usize)>> for InputError<I> { + #[inline] + fn convert(self) -> InputError<(I, usize)> { + InputError { + input: (self.input, 0), + kind: self.kind, + } + } +} + +impl<I: Clone> ErrorConvert<InputError<I>> for InputError<(I, usize)> { + #[inline] + fn convert(self) -> InputError<I> { + InputError { + input: self.input.0, + kind: self.kind, + } + } +} + +/// The Display implementation allows the `std::error::Error` implementation +impl<I: Clone + fmt::Display> fmt::Display for InputError<I> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} error starting at: {}", + self.kind.description(), + self.input + ) + } +} + +#[cfg(feature = "std")] +impl<I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error + for InputError<I> +{ +} + +impl<I> ParserError<I> for () { + #[inline] + fn from_error_kind(_: &I, _: ErrorKind) -> Self {} + + #[inline] + fn append(self, _: &I, _: ErrorKind) -> Self {} +} + +impl<I, C> AddContext<I, C> for () {} + +impl<I, E> FromExternalError<I, E> for () { + #[inline] + fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self {} +} + +impl ErrorConvert<()> for () { + #[inline] + fn convert(self) {} +} + +/// Accumulate context while backtracking errors +#[derive(Debug)] +pub struct ContextError<C = StrContext> { + #[cfg(feature = "alloc")] + context: crate::lib::std::vec::Vec<C>, + #[cfg(not(feature = "alloc"))] + context: core::marker::PhantomData<C>, + #[cfg(feature = "std")] + cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>, +} + +impl<C> ContextError<C> { + /// Create an empty error + #[inline] + pub fn new() -> Self { + Self { + context: Default::default(), + #[cfg(feature = "std")] + cause: None, + } + } + + /// Access context from [`Parser::context`] + #[inline] + #[cfg(feature = "alloc")] + pub fn context(&self) -> impl Iterator<Item = &C> { + self.context.iter() + } + + /// Originating [`std::error::Error`] + #[inline] + #[cfg(feature = "std")] + pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { + self.cause.as_deref() + } +} + +impl<C: Clone> Clone for ContextError<C> { + fn clone(&self) -> Self { + Self { + context: self.context.clone(), + #[cfg(feature = "std")] + cause: self.cause.as_ref().map(|e| e.to_string().into()), + } + } +} + +impl<C> Default for ContextError<C> { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl<I, 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 { + self + } + + #[inline] + fn or(self, other: Self) -> Self { + other + } +} + +impl<C, I> AddContext<I, C> for ContextError<C> { + #[inline] + fn add_context(mut self, _input: &I, ctx: C) -> Self { + #[cfg(feature = "alloc")] + self.context.push(ctx); + self + } +} + +#[cfg(feature = "std")] +impl<C, I, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> + for ContextError<C> +{ + #[inline] + fn from_external_error(_input: &I, _kind: ErrorKind, e: E) -> Self { + let mut err = Self::new(); + { + err.cause = Some(Box::new(e)); + } + err + } +} + +// HACK: This is more general than `std`, making the features non-additive +#[cfg(not(feature = "std"))] +impl<C, I, E: Send + Sync + 'static> FromExternalError<I, E> for ContextError<C> { + #[inline] + fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self { + let err = Self::new(); + err + } +} + +// For tests +impl<C: core::cmp::PartialEq> core::cmp::PartialEq for ContextError<C> { + fn eq(&self, other: &Self) -> bool { + #[cfg(feature = "alloc")] + { + if self.context != other.context { + return false; + } + } + #[cfg(feature = "std")] + { + if self.cause.as_ref().map(ToString::to_string) + != other.cause.as_ref().map(ToString::to_string) + { + return false; + } + } + + true + } +} + +impl crate::lib::std::fmt::Display for ContextError<StrContext> { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + #[cfg(feature = "alloc")] + { + let expression = self.context().find_map(|c| match c { + StrContext::Label(c) => Some(c), + _ => None, + }); + let expected = self + .context() + .filter_map(|c| match c { + StrContext::Expected(c) => Some(c), + _ => None, + }) + .collect::<crate::lib::std::vec::Vec<_>>(); + + let mut newline = false; + + if let Some(expression) = expression { + newline = true; + + write!(f, "invalid {}", expression)?; + } + + if !expected.is_empty() { + if newline { + writeln!(f)?; + } + newline = true; + + write!(f, "expected ")?; + for (i, expected) in expected.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{}", expected)?; + } + } + #[cfg(feature = "std")] + { + if let Some(cause) = self.cause() { + if newline { + writeln!(f)?; + } + write!(f, "{}", cause)?; + } + } + } + + Ok(()) + } +} + +/// Additional parse context for [`ContextError`] added via [`Parser::context`] +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum StrContext { + /// Description of what is currently being parsed + Label(&'static str), + /// Grammar item that was expected + Expected(StrContextValue), +} + +impl crate::lib::std::fmt::Display for StrContext { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + match self { + Self::Label(name) => write!(f, "invalid {name}"), + Self::Expected(value) => write!(f, "expected {value}"), + } + } +} + +/// See [`StrContext`] +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum StrContextValue { + /// A [`char`] token + CharLiteral(char), + /// A [`&str`] token + StringLiteral(&'static str), + /// A description of what was being parsed + Description(&'static str), +} + +impl From<char> for StrContextValue { + #[inline] + fn from(inner: char) -> Self { + Self::CharLiteral(inner) + } +} + +impl From<&'static str> for StrContextValue { + #[inline] + fn from(inner: &'static str) -> Self { + Self::StringLiteral(inner) + } +} + +impl crate::lib::std::fmt::Display for StrContextValue { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + match self { + Self::CharLiteral('\n') => "newline".fmt(f), + Self::CharLiteral('`') => "'`'".fmt(f), + 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), + } + } +} + +/// Trace all error paths, particularly for tests +#[derive(Debug)] +#[cfg(feature = "std")] +pub enum TreeError<I, C = StrContext> { + /// Initial error that kicked things off + Base(TreeErrorBase<I>), + /// Traces added to the error while walking back up the stack + Stack { + /// Initial error that kicked things off + base: Box<Self>, + /// Traces added to the error while walking back up the stack + stack: Vec<TreeErrorFrame<I, C>>, + }, + /// All failed branches of an `alt` + Alt(Vec<Self>), +} + +/// See [`TreeError::Stack`] +#[derive(Debug)] +#[cfg(feature = "std")] +pub enum TreeErrorFrame<I, C = StrContext> { + /// See [`ParserError::append`] + Kind(TreeErrorBase<I>), + /// See [`AddContext::add_context`] + Context(TreeErrorContext<I, C>), +} + +/// See [`TreeErrorFrame::Kind`], [`ParserError::append`] +#[derive(Debug)] +#[cfg(feature = "std")] +pub struct TreeErrorBase<I> { + /// Parsed input, at the location where the error occurred + pub input: I, + /// Debug context + pub kind: ErrorKind, + /// See [`FromExternalError::from_external_error`] + pub cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>, +} + +/// See [`TreeErrorFrame::Context`], [`AddContext::add_context`] +#[derive(Debug)] +#[cfg(feature = "std")] +pub struct TreeErrorContext<I, C = StrContext> { + /// Parsed input, at the location where the error occurred + pub input: I, + /// See [`AddContext::add_context`] + pub context: C, +} + +#[cfg(feature = "std")] +impl<'i, I: ToOwned, C> TreeError<&'i I, C> +where + <I as ToOwned>::Owned: Clone, +{ + /// Obtaining ownership + pub fn into_owned(self) -> TreeError<<I as ToOwned>::Owned, C> { + self.map_input(ToOwned::to_owned) + } +} + +#[cfg(feature = "std")] +impl<I, C> TreeError<I, C> +where + I: Clone, +{ + /// Translate the input type + pub fn map_input<I2: Clone, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> { + match self { + TreeError::Base(base) => TreeError::Base(TreeErrorBase { + input: op(base.input), + kind: base.kind, + cause: base.cause, + }), + TreeError::Stack { base, stack } => { + let base = Box::new(base.map_input(op.clone())); + let stack = stack + .into_iter() + .map(|frame| match frame { + TreeErrorFrame::Kind(kind) => TreeErrorFrame::Kind(TreeErrorBase { + input: op(kind.input), + kind: kind.kind, + cause: kind.cause, + }), + TreeErrorFrame::Context(context) => { + TreeErrorFrame::Context(TreeErrorContext { + input: op(context.input), + context: context.context, + }) + } + }) + .collect(); + TreeError::Stack { base, stack } + } + TreeError::Alt(alt) => { + TreeError::Alt(alt.into_iter().map(|e| e.map_input(op.clone())).collect()) + } + } + } + + fn append_frame(self, frame: TreeErrorFrame<I, C>) -> Self { + match self { + TreeError::Stack { base, mut stack } => { + stack.push(frame); + TreeError::Stack { base, stack } + } + base => TreeError::Stack { + base: Box::new(base), + stack: vec![frame], + }, + } + } +} + +#[cfg(feature = "std")] +impl<I, C> ParserError<I> for TreeError<I, C> +where + I: Clone, +{ + fn from_error_kind(input: &I, kind: ErrorKind) -> Self { + TreeError::Base(TreeErrorBase { + input: input.clone(), + kind, + cause: None, + }) + } + + fn append(self, input: &I, kind: ErrorKind) -> Self { + let frame = TreeErrorFrame::Kind(TreeErrorBase { + input: input.clone(), + kind, + cause: None, + }); + self.append_frame(frame) + } + + fn or(self, other: Self) -> Self { + match (self, other) { + (TreeError::Alt(mut first), TreeError::Alt(second)) => { + // Just in case an implementation does a divide-and-conquer algorithm + // + // To prevent mixing `alt`s at different levels, parsers should + // `alt_err.append(input, ErrorKind::Alt)`. + first.extend(second); + TreeError::Alt(first) + } + (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => { + alt.push(new); + TreeError::Alt(alt) + } + (first, second) => TreeError::Alt(vec![first, second]), + } + } +} + +#[cfg(feature = "std")] +impl<I, C> AddContext<I, C> for TreeError<I, C> +where + I: Clone, +{ + fn add_context(self, input: &I, context: C) -> Self { + let frame = TreeErrorFrame::Context(TreeErrorContext { + input: input.clone(), + context, + }); + self.append_frame(frame) + } +} + +#[cfg(feature = "std")] +impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C> +where + I: Clone, +{ + fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self { + TreeError::Base(TreeErrorBase { + input: input.clone(), + kind, + cause: Some(Box::new(e)), + }) + } +} + +#[cfg(feature = "std")] +impl<I, C> TreeError<I, C> +where + I: Clone + crate::lib::std::fmt::Display, + C: fmt::Display, +{ + fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result { + let child_indent = indent + 2; + match self { + TreeError::Base(base) => { + writeln!(f, "{:indent$}{base}", "")?; + } + TreeError::Stack { base, stack } => { + base.write(f, indent)?; + for (level, frame) in stack.iter().enumerate() { + match frame { + TreeErrorFrame::Kind(frame) => { + writeln!(f, "{:child_indent$}{level}: {frame}", "")?; + } + TreeErrorFrame::Context(frame) => { + writeln!(f, "{:child_indent$}{level}: {frame}", "")?; + } + } + } + } + TreeError::Alt(alt) => { + writeln!(f, "{:indent$}during one of:", "")?; + for child in alt { + child.write(f, child_indent)?; + } + } + } + + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<I: 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}")?; + } else { + let kind = self.kind.description(); + write!(f, "in {kind}")?; + } + let input = abbreviate(self.input.to_string()); + write!(f, " at '{input}'")?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl<I: 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()); + write!(f, "{context} at '{input}'")?; + Ok(()) + } +} + +#[cfg(feature = "std")] +impl< + I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static, + C: fmt::Display + fmt::Debug, + > std::error::Error for TreeError<I, C> +{ +} + +#[cfg(feature = "std")] +fn abbreviate(input: String) -> String { + let mut abbrev = None; + + if let Some((line, _)) = input.split_once('\n') { + abbrev = Some(line); + } + + let max_len = 20; + let current = abbrev.unwrap_or(&input); + if max_len < current.len() { + if let Some((index, _)) = current.char_indices().nth(max_len) { + abbrev = Some(¤t[..index]); + } + } + + if let Some(abbrev) = abbrev { + format!("{abbrev}...") + } else { + input + } +} + +#[cfg(feature = "std")] +impl<I: 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)] +#[allow(missing_docs)] +pub enum ErrorKind { + Assert, + Token, + Tag, + Alt, + Many, + Eof, + Slice, + Complete, + Not, + Verify, + Fail, +} + +impl ErrorKind { + #[rustfmt::skip] + /// Converts an `ErrorKind` to a text description + pub fn description(&self) -> &str { + match *self { + ErrorKind::Assert => "assert", + ErrorKind::Token => "token", + ErrorKind::Tag => "tag", + ErrorKind::Alt => "alternative", + ErrorKind::Many => "many", + ErrorKind::Eof => "end of file", + ErrorKind::Slice => "slice", + ErrorKind::Complete => "complete", + ErrorKind::Not => "negation", + ErrorKind::Verify => "predicate verification", + ErrorKind::Fail => "fail", + } + } +} + +impl<I> ParserError<I> for ErrorKind { + #[inline] + fn from_error_kind(_input: &I, kind: ErrorKind) -> Self { + kind + } + + #[inline] + fn append(self, _: &I, _: ErrorKind) -> Self { + self + } +} + +impl<I, 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 + #[inline] + fn from_external_error(_input: &I, kind: ErrorKind, _e: E) -> Self { + kind + } +} + +/// 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) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ErrorKind {} + +/// See [`Parser::parse`] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ParseError<I, E> { + input: I, + offset: usize, + inner: E, +} + +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); + Self { + input, + offset, + inner, + } + } +} + +impl<I, E> ParseError<I, E> { + /// The [`Stream`] at the initial location when parsing started + #[inline] + pub fn input(&self) -> &I { + &self.input + } + + /// The location in [`ParseError::input`] where parsing failed + /// + /// **Note:** This is an offset, not an index, and may point to the end of input + /// (`input.len()`) on eof errors. + #[inline] + pub fn offset(&self) -> usize { + self.offset + } + + /// The original [`ParserError`] + #[inline] + pub fn inner(&self) -> &E { + &self.inner + } + + /// The original [`ParserError`] + #[inline] + pub fn into_inner(self) -> E { + self.inner + } +} + +impl<I, E> core::fmt::Display for ParseError<I, E> +where + I: AsBStr, + E: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let input = self.input.as_bstr(); + let span_start = self.offset; + let span_end = span_start; + #[cfg(feature = "std")] + if input.contains(&b'\n') { + let (line_idx, col_idx) = translate_position(input, span_start); + let line_num = line_idx + 1; + let col_num = col_idx + 1; + let gutter = line_num.to_string().len(); + let content = input + .split(|c| *c == b'\n') + .nth(line_idx) + .expect("valid line number"); + + writeln!(f, "parse error at line {}, column {}", line_num, col_num)?; + // | + for _ in 0..=gutter { + write!(f, " ")?; + } + writeln!(f, "|")?; + + // 1 | 00:32:00.a999999 + write!(f, "{} | ", line_num)?; + writeln!(f, "{}", String::from_utf8_lossy(content))?; + + // | ^ + for _ in 0..=gutter { + write!(f, " ")?; + } + write!(f, "|")?; + for _ in 0..=col_idx { + write!(f, " ")?; + } + // 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())) { + write!(f, "^")?; + } + writeln!(f)?; + } else { + let content = input; + writeln!(f, "{}", String::from_utf8_lossy(content))?; + for _ in 0..=span_start { + write!(f, " ")?; + } + // 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())) { + write!(f, "^")?; + } + writeln!(f)?; + } + write!(f, "{}", self.inner)?; + + Ok(()) + } +} + +#[cfg(feature = "std")] +fn translate_position(input: &[u8], index: usize) -> (usize, usize) { + if input.is_empty() { + return (0, index); + } + + let safe_index = index.min(input.len() - 1); + let column_offset = index - safe_index; + let index = safe_index; + + let nl = input[0..index] + .iter() + .rev() + .enumerate() + .find(|(_, b)| **b == b'\n') + .map(|(nl, _)| index - nl - 1); + let line_start = match nl { + Some(nl) => nl + 1, + None => 0, + }; + let line = input[0..line_start].iter().filter(|b| **b == b'\n').count(); + + // HACK: This treats byte offset and column offsets the same + let column = crate::lib::std::str::from_utf8(&input[line_start..=index]) + .map(|s| s.chars().count() - 1) + .unwrap_or_else(|_| index - line_start); + let column = column + column_offset; + + (line, column) +} + +#[cfg(test)] +#[cfg(feature = "std")] +mod test_translate_position { + use super::*; + + #[test] + fn empty() { + let input = b""; + let index = 0; + let position = translate_position(&input[..], index); + assert_eq!(position, (0, 0)); + } + + #[test] + fn start() { + let input = b"Hello"; + let index = 0; + let position = translate_position(&input[..], index); + assert_eq!(position, (0, 0)); + } + + #[test] + fn end() { + let input = b"Hello"; + let index = input.len() - 1; + let position = translate_position(&input[..], index); + assert_eq!(position, (0, input.len() - 1)); + } + + #[test] + fn after() { + let input = b"Hello"; + let index = input.len(); + let position = translate_position(&input[..], index); + assert_eq!(position, (0, input.len())); + } + + #[test] + fn first_line() { + let input = b"Hello\nWorld\n"; + let index = 2; + let position = translate_position(&input[..], index); + assert_eq!(position, (0, 2)); + } + + #[test] + fn end_of_line() { + let input = b"Hello\nWorld\n"; + let index = 5; + let position = translate_position(&input[..], index); + assert_eq!(position, (0, 5)); + } + + #[test] + fn start_of_second_line() { + let input = b"Hello\nWorld\n"; + let index = 6; + let position = translate_position(&input[..], index); + assert_eq!(position, (1, 0)); + } + + #[test] + fn second_line() { + let input = b"Hello\nWorld\n"; + let index = 8; + let position = translate_position(&input[..], index); + assert_eq!(position, (1, 2)); + } +} + +/// Creates a parse error from a [`ErrorKind`] +/// and the position in the input +#[cfg(test)] +macro_rules! error_position( + ($input:expr, $code:expr) => ({ + $crate::error::ParserError::from_error_kind($input, $code) + }); +); + +#[cfg(test)] +macro_rules! error_node_position( + ($input:expr, $code:expr, $next:expr) => ({ + $crate::error::ParserError::append($next, $input, $code) + }); +); diff --git a/vendor/winnow/src/lib.rs b/vendor/winnow/src/lib.rs new file mode 100644 index 0000000..4a21b9e --- /dev/null +++ b/vendor/winnow/src/lib.rs @@ -0,0 +1,251 @@ +//! > winnow, making parsing a breeze +//! +//! `winnow` is a parser combinator library +//! +//! Quick links: +//! - [List of combinators][crate::combinator] +//! - [Tutorial][_tutorial::chapter_0] +//! - [Special Topics][_topic] +//! - [Discussions](https://github.com/winnow-rs/winnow/discussions) +//! +//! ## Aspirations +//! +//! `winnow` aims to be your "do everything" parser, much like people treat regular expressions. +//! +//! In roughly priority order: +//! 1. Support writing parser declaratively while not getting in the way of imperative-style +//! parsing when needed, working as an open-ended toolbox rather than a close-ended framework. +//! 2. Flexible enough to be used for any application, including parsing binary data, strings, or +//! separate lexing and parsing phases +//! 3. Zero-cost abstractions, making it easy to write high performance parsers +//! 4. Easy to use, making it trivial for one-off uses +//! +//! In addition: +//! - Resilient maintainership, including +//! - Willing to break compatibility rather than batching up breaking changes in large releases +//! - Leverage feature flags to keep one active branch +//! - We will support the last 6 months of rust releases (MSRV, currently 1.64.0) +//! +//! See also [Special Topic: Why winnow?][crate::_topic::why] +//! +//! ## Example +//! +//! Run +//! ```console +//! $ cargo add winnow +//! ``` +//! +//! Then use it to parse: +//! ```rust +//! # #[cfg(feature = "alloc")] { +#![doc = include_str!("../examples/css/parser.rs")] +//! # } +//! ``` +//! +//! See also the [Tutorial][_tutorial::chapter_0] and [Special Topics][_topic] + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(extended_key_value_attributes))] +#![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))] +#[cfg(feature = "alloc")] +#[cfg_attr(test, macro_use)] +extern crate alloc; +#[cfg(doctest)] +extern crate doc_comment; + +#[cfg(doctest)] +doc_comment::doctest!("../README.md"); + +/// Lib module to re-export everything needed from `std` or `core`/`alloc`. This is how `serde` does +/// it, albeit there it is not public. +#[doc(hidden)] +pub(crate) mod lib { + /// `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 { + #[doc(hidden)] + #[cfg(not(feature = "alloc"))] + pub use core::borrow; + + #[cfg(feature = "alloc")] + #[doc(hidden)] + pub 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; + } + } + + #[cfg(feature = "std")] + /// internal std exports for `no_std` compatibility + pub 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, + }; + + /// internal reproduction of std prelude + #[doc(hidden)] + pub mod prelude { + pub use std::prelude as v1; + } + } +} + +#[macro_use] +mod macros; + +#[macro_use] +pub mod error; + +mod parser; + +pub mod stream; + +pub mod ascii; +pub mod binary; +pub mod combinator; +pub mod token; +pub mod trace; + +#[cfg(feature = "unstable-doc")] +pub mod _topic; +#[cfg(feature = "unstable-doc")] +pub mod _tutorial; + +/// Core concepts available for glob import +/// +/// Including +/// - [`StreamIsPartial`][crate::stream::StreamIsPartial] +/// - [`Parser`] +/// +/// ## Example +/// +/// ```rust +/// use winnow::prelude::*; +/// +/// fn parse_data(input: &mut &str) -> PResult<u64> { +/// // ... +/// # winnow::ascii::dec_uint(input) +/// } +/// +/// fn main() { +/// let result = parse_data.parse("100"); +/// assert_eq!(result, Ok(100)); +/// } +/// ``` +pub mod prelude { + pub use crate::stream::StreamIsPartial as _; + pub use crate::IResult; + pub use crate::PResult; + pub use crate::Parser; +} + +pub use error::IResult; +pub use error::PResult; +pub use parser::*; +pub use stream::BStr; +pub use stream::Bytes; +pub use stream::Located; +pub use stream::Partial; +pub use stream::Stateful; +pub use stream::Str; diff --git a/vendor/winnow/src/macros.rs b/vendor/winnow/src/macros.rs new file mode 100644 index 0000000..35279ba --- /dev/null +++ b/vendor/winnow/src/macros.rs @@ -0,0 +1,79 @@ +/// `match` for parsers +/// +/// When parsers have unique prefixes to test for, this offers better performance over +/// [`alt`][crate::combinator::alt] though it might be at the cost of duplicating parts of your grammar +/// if you needed to [`peek`][crate::combinator::peek]. +/// +/// For tight control over the error in a catch-all case, use [`fail`][crate::combinator::fail]. +/// +/// # Example +/// +/// ```rust +/// use winnow::prelude::*; +/// use winnow::combinator::dispatch; +/// # use winnow::token::any; +/// # use winnow::combinator::peek; +/// # use winnow::combinator::preceded; +/// # use winnow::combinator::success; +/// # use winnow::combinator::fail; +/// +/// fn escaped(input: &mut &str) -> PResult<char> { +/// preceded('\\', escape_seq_char).parse_next(input) +/// } +/// +/// fn escape_seq_char(input: &mut &str) -> PResult<char> { +/// dispatch! {any; +/// 'b' => success('\u{8}'), +/// 'f' => success('\u{c}'), +/// 'n' => success('\n'), +/// 'r' => success('\r'), +/// 't' => success('\t'), +/// '\\' => success('\\'), +/// '"' => success('"'), +/// _ => fail::<_, char, _>, +/// } +/// .parse_next(input) +/// } +/// +/// assert_eq!(escaped.parse_peek("\\nHello"), Ok(("Hello", '\n'))); +/// ``` +#[macro_export] +#[doc(hidden)] // forced to be visible in intended location +macro_rules! dispatch { + ($match_parser: expr; $( $pat:pat $(if $pred:expr)? => $expr: expr ),+ $(,)? ) => { + $crate::trace::trace("dispatch", move |i: &mut _| + { + use $crate::Parser; + let initial = $match_parser.parse_next(i)?; + match initial { + $( + $pat $(if $pred)? => $expr.parse_next(i), + )* + } + }) + } +} + +macro_rules! succ ( + (0, $submac:ident ! ($($rest:tt)*)) => ($submac!(1, $($rest)*)); + (1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*)); + (2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*)); + (3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*)); + (4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*)); + (5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*)); + (6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*)); + (7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*)); + (8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*)); + (9, $submac:ident ! ($($rest:tt)*)) => ($submac!(10, $($rest)*)); + (10, $submac:ident ! ($($rest:tt)*)) => ($submac!(11, $($rest)*)); + (11, $submac:ident ! ($($rest:tt)*)) => ($submac!(12, $($rest)*)); + (12, $submac:ident ! ($($rest:tt)*)) => ($submac!(13, $($rest)*)); + (13, $submac:ident ! ($($rest:tt)*)) => ($submac!(14, $($rest)*)); + (14, $submac:ident ! ($($rest:tt)*)) => ($submac!(15, $($rest)*)); + (15, $submac:ident ! ($($rest:tt)*)) => ($submac!(16, $($rest)*)); + (16, $submac:ident ! ($($rest:tt)*)) => ($submac!(17, $($rest)*)); + (17, $submac:ident ! ($($rest:tt)*)) => ($submac!(18, $($rest)*)); + (18, $submac:ident ! ($($rest:tt)*)) => ($submac!(19, $($rest)*)); + (19, $submac:ident ! ($($rest:tt)*)) => ($submac!(20, $($rest)*)); + (20, $submac:ident ! ($($rest:tt)*)) => ($submac!(21, $($rest)*)); +); diff --git a/vendor/winnow/src/parser.rs b/vendor/winnow/src/parser.rs new file mode 100644 index 0000000..b59e4cd --- /dev/null +++ b/vendor/winnow/src/parser.rs @@ -0,0 +1,977 @@ +//! Basic types to build the parsers + +use crate::combinator::*; +use crate::error::{AddContext, FromExternalError, IResult, PResult, ParseError, ParserError}; +use crate::stream::{AsChar, Compare, Location, ParseSlice, Stream, StreamIsPartial}; + +/// Core trait for parsing +/// +/// The simplest way to implement a `Parser` is with a function +/// ```rust +/// use winnow::prelude::*; +/// +/// fn success(input: &mut &str) -> PResult<()> { +/// let output = (); +/// Ok(output) +/// } +/// +/// let (input, output) = success.parse_peek("Hello").unwrap(); +/// assert_eq!(input, "Hello"); // We didn't consume any input +/// ``` +/// +/// which can be made stateful by returning a function +/// ```rust +/// use winnow::prelude::*; +/// +/// fn success<O: Clone>(output: O) -> impl FnMut(&mut &str) -> PResult<O> { +/// move |input: &mut &str| { +/// let output = output.clone(); +/// Ok(output) +/// } +/// } +/// +/// let (input, output) = success("World").parse_peek("Hello").unwrap(); +/// assert_eq!(input, "Hello"); // We didn't consume any input +/// assert_eq!(output, "World"); +/// ``` +/// +/// 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] +pub trait Parser<I, O, E> { + /// Parse all of `input`, generating `O` from it + #[inline] + fn parse(&mut self, mut input: I) -> Result<O, ParseError<I, E>> + where + Self: core::marker::Sized, + I: Stream, + // Force users to deal with `Incomplete` when `StreamIsPartial<true>` + I: StreamIsPartial, + I: Clone, + E: ParserError<I>, + { + debug_assert!( + !I::is_partial_supported(), + "partial streams need to handle `ErrMode::Incomplete`" + ); + + let start = input.checkpoint(); + let (o, _) = (self.by_ref(), crate::combinator::eof) + .parse_next(&mut input) + .map_err(|e| { + let e = e + .into_inner() + .expect("complete parsers should not report `ErrMode::Incomplete(_)`"); + ParseError::new(input, start, e) + })?; + Ok(o) + } + + /// Take tokens from the [`Stream`], turning it into the output + /// + /// This includes advancing the [`Stream`] to the next location. + /// + /// On error, `input` will be left pointing at the error location. + fn parse_next(&mut self, input: &mut I) -> PResult<O, E>; + + /// Take tokens from the [`Stream`], turning it into the output + /// + /// This includes advancing the [`Stream`] to the next location. + #[inline(always)] + fn parse_peek(&mut self, mut input: I) -> IResult<I, O, E> { + match self.parse_next(&mut input) { + Ok(o) => Ok((input, o)), + Err(err) => Err(err), + } + } + + /// Treat `&mut Self` as a parser + /// + /// This helps when needing to move a `Parser` when all you have is a `&mut Parser`. + /// + /// # 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 + /// [`Parser::complete_err`]: + /// ```rust,compile_fail + /// # use winnow::prelude::*; + /// # use winnow::Parser; + /// # use winnow::error::ParserError; + /// # use winnow::binary::length_data; + /// 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 o = g.complete_err().parse_next(&mut data)?; + /// Ok(o) + /// } + /// } + /// ``` + /// + /// By adding `by_ref`, we can make this work: + /// ```rust + /// # use winnow::prelude::*; + /// # use winnow::Parser; + /// # use winnow::error::ParserError; + /// # use winnow::binary::length_data; + /// 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 o = g.by_ref().complete_err().parse_next(&mut data)?; + /// Ok(o) + /// } + /// } + /// ``` + #[inline(always)] + fn by_ref(&mut self) -> ByRef<'_, Self> + where + Self: core::marker::Sized, + { + ByRef::new(self) + } + + /// Produce the provided value + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::alpha1; + /// # fn main() { + /// + /// let mut parser = alpha1.value(1234); + /// + /// assert_eq!(parser.parse_peek("abcd"), Ok(("", 1234))); + /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); + /// # } + /// ``` + #[doc(alias = "to")] + #[inline(always)] + fn value<O2>(self, val: O2) -> Value<Self, I, O, O2, E> + where + Self: core::marker::Sized, + O2: Clone, + { + Value::new(self, val) + } + + /// Discards the output of the `Parser` + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::alpha1; + /// # fn main() { + /// + /// let mut parser = alpha1.void(); + /// + /// assert_eq!(parser.parse_peek("abcd"), Ok(("", ()))); + /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); + /// # } + /// ``` + #[inline(always)] + fn void(self) -> Void<Self, I, O, E> + where + Self: core::marker::Sized, + { + Void::new(self) + } + + /// Convert the parser's output to another type using [`std::convert::From`] + /// + /// # Example + /// + /// ```rust + /// # use winnow::prelude::*; + /// # use winnow::error::InputError; + /// use winnow::ascii::alpha1; + /// # fn main() { + /// + /// fn parser1<'s>(i: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { + /// alpha1(i) + /// } + /// + /// let mut parser2 = parser1.output_into(); + /// + /// // the parser converts the &str output of the child parser into a Vec<u8> + /// let bytes: IResult<&str, Vec<u8>> = parser2.parse_peek("abcd"); + /// assert_eq!(bytes, Ok(("", vec![97, 98, 99, 100]))); + /// # } + /// ``` + #[inline(always)] + fn output_into<O2>(self) -> OutputInto<Self, I, O, O2, E> + where + Self: core::marker::Sized, + O: Into<O2>, + { + OutputInto::new(self) + } + + /// Produce the consumed input as produced value. + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::{alpha1}; + /// use winnow::combinator::separated_pair; + /// # fn main() { + /// + /// let mut parser = separated_pair(alpha1, ',', alpha1).recognize(); + /// + /// assert_eq!(parser.parse_peek("abcd,efgh"), Ok(("", "abcd,efgh"))); + /// assert_eq!(parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify)))); + /// # } + /// ``` + #[doc(alias = "concat")] + #[inline(always)] + fn recognize(self) -> Recognize<Self, I, O, E> + where + Self: core::marker::Sized, + I: Stream, + { + Recognize::new(self) + } + + /// Produce the consumed input with the output + /// + /// Functions similarly to [recognize][Parser::recognize] except it + /// returns the parser output as well. + /// + /// This can be useful especially in cases where the output is not the same type + /// as the input, or the input is a user defined type. + /// + /// Returned tuple is of the format `(produced output, consumed input)`. + /// + /// # Example + /// + /// ```rust + /// # use winnow::prelude::*; + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError}; + /// use winnow::ascii::{alpha1}; + /// use winnow::token::tag; + /// 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(); + /// + /// 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)))); + /// + /// // 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); + /// + /// assert_eq!(recognize_parser.parse_peek("1234"), consumed_parser.parse_peek("1234")); + /// assert_eq!(recognize_parser.parse_peek("abcd"), consumed_parser.parse_peek("abcd")); + /// ``` + #[doc(alias = "consumed")] + #[inline(always)] + fn with_recognized(self) -> WithRecognized<Self, I, O, E> + where + Self: core::marker::Sized, + I: Stream, + { + WithRecognized::new(self) + } + + /// Produce the location of the consumed input as produced value. + /// + /// # Example + /// + /// ```rust + /// # use winnow::prelude::*; + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream}; + /// use winnow::stream::Located; + /// 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)))); + /// ``` + #[inline(always)] + fn span(self) -> Span<Self, I, O, E> + where + Self: core::marker::Sized, + I: Stream + Location, + { + Span::new(self) + } + + /// Produce the location of consumed input with the output + /// + /// Functions similarly to [`Parser::span`] except it + /// returns the parser output as well. + /// + /// This can be useful especially in cases where the output is not the same type + /// as the input, or the input is a user defined type. + /// + /// Returned tuple is of the format `(produced output, consumed input)`. + /// + /// # Example + /// + /// ```rust + /// # use winnow::prelude::*; + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream}; + /// use winnow::stream::Located; + /// use winnow::ascii::alpha1; + /// use winnow::token::tag; + /// use winnow::combinator::separated_pair; + /// + /// fn inner_parser<'s>(input: &mut Located<&'s str>) -> PResult<bool, InputError<Located<&'s str>>> { + /// "1234".value(true).parse_next(input) + /// } + /// + /// # fn main() { + /// + /// 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)))); + /// + /// // 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 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"))); + /// # } + /// ``` + #[inline(always)] + fn with_span(self) -> WithSpan<Self, I, O, E> + where + Self: core::marker::Sized, + I: Stream + Location, + { + WithSpan::new(self) + } + + /// Maps a function over the output of a parser + /// + /// # Example + /// + /// ```rust + /// use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::digit1; + /// # fn main() { + /// + /// let mut parser = digit1.map(|s: &str| s.len()); + /// + /// // the parser will count how many characters were returned by digit1 + /// assert_eq!(parser.parse_peek("123456"), Ok(("", 6))); + /// + /// // this will fail if digit1 fails + /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice)))); + /// # } + /// ``` + #[inline(always)] + fn map<G, O2>(self, map: G) -> Map<Self, G, I, O, O2, E> + where + G: Fn(O) -> O2, + Self: core::marker::Sized, + { + Map::new(self, map) + } + + /// Applies a function returning a `Result` over the output of a parser. + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::digit1; + /// # fn main() { + /// + /// let mut parse = digit1.try_map(|s: &str| s.parse::<u8>()); + /// + /// // the parser will convert the result of digit1 to a number + /// assert_eq!(parse.parse_peek("123"), Ok(("", 123))); + /// + /// // this will fail if digit1 fails + /// assert_eq!(parse.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice)))); + /// + /// // this will fail if the mapped function fails (a `u8` is too small to hold `123456`) + /// assert_eq!(parse.parse_peek("123456"), Err(ErrMode::Backtrack(InputError::new("123456", ErrorKind::Verify)))); + /// # } + /// ``` + #[inline(always)] + fn try_map<G, O2, E2>(self, map: G) -> 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) + } + + /// Apply both [`Parser::verify`] and [`Parser::map`]. + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::digit1; + /// # fn main() { + /// + /// let mut parse = digit1.verify_map(|s: &str| s.parse::<u8>().ok()); + /// + /// // the parser will convert the result of digit1 to a number + /// assert_eq!(parse.parse_peek("123"), Ok(("", 123))); + /// + /// // this will fail if digit1 fails + /// assert_eq!(parse.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice)))); + /// + /// // this will fail if the mapped function fails (a `u8` is too small to hold `123456`) + /// assert_eq!(parse.parse_peek("123456"), Err(ErrMode::Backtrack(InputError::new("123456", ErrorKind::Verify)))); + /// # } + /// ``` + #[doc(alias = "satisfy_map")] + #[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> + where + Self: core::marker::Sized, + G: FnMut(O) -> Option<O2>, + I: Stream, + E: ParserError<I>, + { + VerifyMap::new(self, map) + } + + /// Creates a parser from the output of this one + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser}; + /// use winnow::token::take; + /// use winnow::binary::u8; + /// + /// fn length_data<'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)))); + /// ``` + /// + /// which is the same as + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser}; + /// use winnow::token::take; + /// use winnow::binary::u8; + /// + /// fn length_data<'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)))); + /// ``` + #[inline(always)] + fn flat_map<G, H, O2>(self, map: G) -> 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) + } + + /// Applies a second parser over the output of the first one + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::digit1; + /// use winnow::token::take; + /// # fn main() { + /// + /// let mut digits = take(5u8).and_then(digit1); + /// + /// assert_eq!(digits.parse_peek("12345"), Ok(("", "12345"))); + /// assert_eq!(digits.parse_peek("123ab"), Ok(("", "123"))); + /// assert_eq!(digits.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Slice)))); + /// # } + /// ``` + #[inline(always)] + fn and_then<G, O2>(self, inner: G) -> 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) + } + + /// Apply [`std::str::FromStr`] to the output of the parser + /// + /// # Example + /// + /// ```rust + /// # use winnow::prelude::*; + /// use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// use winnow::ascii::digit1; + /// + /// fn parser<'s>(input: &mut &'s str) -> PResult<u64, InputError<&'s str>> { + /// digit1.parse_to().parse_next(input) + /// } + /// + /// // the parser will count how many characters were returned by digit1 + /// assert_eq!(parser.parse_peek("123456"), Ok(("", 123456))); + /// + /// // this will fail if digit1 fails + /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice)))); + /// ``` + #[doc(alias = "from_str")] + #[inline(always)] + fn parse_to<O2>(self) -> ParseTo<Self, I, O, O2, E> + where + Self: core::marker::Sized, + I: Stream, + O: ParseSlice<O2>, + E: ParserError<I>, + { + ParseTo::new(self) + } + + /// Returns the output of the child parser if it satisfies a verification function. + /// + /// The verification function takes as argument a reference to the output of the + /// parser. + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; + /// # use winnow::ascii::alpha1; + /// # fn main() { + /// + /// let mut parser = alpha1.verify(|s: &str| s.len() == 4); + /// + /// assert_eq!(parser.parse_peek("abcd"), Ok(("", "abcd"))); + /// assert_eq!(parser.parse_peek("abcde"), Err(ErrMode::Backtrack(InputError::new("abcde", ErrorKind::Verify)))); + /// assert_eq!(parser.parse_peek("123abcd;"),Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); + /// # } + /// ``` + #[doc(alias = "satisfy")] + #[doc(alias = "filter")] + #[inline(always)] + fn verify<G, O2>(self, filter: G) -> Verify<Self, G, I, O, O2, E> + where + Self: core::marker::Sized, + G: Fn(&O2) -> bool, + I: Stream, + O: crate::lib::std::borrow::Borrow<O2>, + O2: ?Sized, + E: ParserError<I>, + { + Verify::new(self, filter) + } + + /// If parsing fails, add context to the error + /// + /// This is used mainly to add user friendly information + /// 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> + where + Self: core::marker::Sized, + I: Stream, + E: AddContext<I, C>, + C: Clone + crate::lib::std::fmt::Debug, + { + Context::new(self, context) + } + + /// Transforms [`Incomplete`][crate::error::ErrMode::Incomplete] into [`Backtrack`][crate::error::ErrMode::Backtrack] + /// + /// # Example + /// + /// ```rust + /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, stream::Partial, Parser}; + /// # use winnow::token::take; + /// # fn main() { + /// + /// let mut parser = take(5u8).complete_err(); + /// + /// assert_eq!(parser.parse_peek(Partial::new("abcdefg")), Ok((Partial::new("fg"), "abcde"))); + /// assert_eq!(parser.parse_peek(Partial::new("abcd")), Err(ErrMode::Backtrack(InputError::new(Partial::new("abcd"), ErrorKind::Complete)))); + /// # } + /// ``` + #[inline(always)] + fn complete_err(self) -> CompleteErr<Self> + where + Self: core::marker::Sized, + { + CompleteErr::new(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> + where + Self: core::marker::Sized, + E: Into<E2>, + { + ErrInto::new(self) + } +} + +impl<'a, I, O, E, F> Parser<I, O, E> for F +where + F: FnMut(&mut I) -> PResult<O, E> + 'a, + I: Stream, +{ + #[inline(always)] + fn parse_next(&mut self, i: &mut I) -> PResult<O, E> { + self(i) + } +} + +/// This is a shortcut for [`one_of`][crate::token::one_of]. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{ErrorKind, InputError}}; +/// fn parser<'s>(i: &mut &'s [u8]) -> PResult<u8, InputError<&'s [u8]>> { +/// 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)))); +/// ``` +impl<I, E> Parser<I, u8, E> for u8 +where + I: StreamIsPartial, + I: Stream<Token = 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) + } +} + +/// This is a shortcut for [`one_of`][crate::token::one_of]. +/// +/// # Example +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{ErrorKind, InputError}}; +/// fn parser<'s>(i: &mut &'s str) -> PResult<char, InputError<&'s str>> { +/// '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)))); +/// ``` +impl<I, E> Parser<I, <I as Stream>::Token, E> for char +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: AsChar + Clone, + 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) + } +} + +/// This is a shortcut for [`tag`][crate::token::tag]. +/// +/// # Example +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::combinator::alt; +/// # use winnow::token::take; +/// +/// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { +/// alt((&"Hello"[..], take(5usize))).parse_next(s) +/// } +/// +/// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..]))); +/// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..]))); +/// assert_eq!(parser.parse_peek(&b"Some"[..]), Err(ErrMode::Backtrack(InputError::new(&b"Some"[..], ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Slice)))); +/// ``` +impl<'s, I, E: ParserError<I>> Parser<I, <I as Stream>::Slice, E> for &'s [u8] +where + I: Compare<&'s [u8]> + StreamIsPartial, + I: Stream, +{ + #[inline(always)] + fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> { + crate::token::tag(*self).parse_next(i) + } +} + +/// This is a shortcut for [`tag`][crate::token::tag]. +/// +/// # Example +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::combinator::alt; +/// # use winnow::token::take; +/// +/// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { +/// alt((b"Hello", take(5usize))).parse_next(s) +/// } +/// +/// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..]))); +/// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..]))); +/// assert_eq!(parser.parse_peek(&b"Some"[..]), Err(ErrMode::Backtrack(InputError::new(&b"Some"[..], ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Slice)))); +/// ``` +impl<'s, I, E: ParserError<I>, const N: usize> Parser<I, <I as Stream>::Slice, E> for &'s [u8; N] +where + I: Compare<&'s [u8; N]> + StreamIsPartial, + I: Stream, +{ + #[inline(always)] + fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> { + crate::token::tag(*self).parse_next(i) + } +} + +/// This is a shortcut for [`tag`][crate::token::tag]. +/// +/// # Example +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; +/// # use winnow::combinator::alt; +/// # use winnow::token::take; +/// +/// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// alt(("Hello", take(5usize))).parse_next(s) +/// } +/// +/// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello"))); +/// assert_eq!(parser.parse_peek("Something"), Ok(("hing", "Somet"))); +/// assert_eq!(parser.parse_peek("Some"), Err(ErrMode::Backtrack(InputError::new("Some", ErrorKind::Slice)))); +/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +impl<'s, I, E: ParserError<I>> Parser<I, <I as Stream>::Slice, E> for &'s str +where + I: Compare<&'s str> + StreamIsPartial, + I: Stream, +{ + #[inline(always)] + fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> { + crate::token::tag(*self).parse_next(i) + } +} + +impl<I, E: ParserError<I>> Parser<I, (), E> for () { + #[inline(always)] + fn parse_next(&mut self, _i: &mut I) -> PResult<(), E> { + Ok(()) + } +} + +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),+,) + where + $($parser: Parser<I, $output, E>),+ + { + #[inline(always)] + fn parse_next(&mut self, i: &mut I) -> PResult<($($output),+,), E> { + let ($(ref mut $parser),+,) = *self; + + $(let $output = $parser.parse_next(i)?;)+ + + Ok(($($output),+,)) + } + } + ) +} + +macro_rules! impl_parser_for_tuples { + ($parser1:ident $output1:ident, $($parser:ident $output:ident),+) => { + impl_parser_for_tuples!(__impl $parser1 $output1; $($parser $output),+); + }; + (__impl $($parser:ident $output:ident),+; $parser1:ident $output1:ident $(,$parser2:ident $output2:ident)*) => { + impl_parser_for_tuple!($($parser $output),+); + impl_parser_for_tuples!(__impl $($parser $output),+, $parser1 $output1; $($parser2 $output2),*); + }; + (__impl $($parser:ident $output:ident),+;) => { + impl_parser_for_tuple!($($parser $output),+); + } +} + +impl_parser_for_tuples!( + P1 O1, + P2 O2, + P3 O3, + P4 O4, + P5 O5, + P6 O6, + P7 O7, + P8 O8, + P9 O9, + P10 O10, + P11 O11, + P12 O12, + P13 O13, + P14 O14, + P15 O15, + P16 O16, + P17 O17, + P18 O18, + P19 O19, + P20 O20, + P21 O21 +); + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +#[cfg(feature = "alloc")] +impl<'a, I, O, E> Parser<I, O, E> for Box<dyn Parser<I, O, E> + 'a> { + #[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`] +#[inline(always)] +pub fn unpeek<'a, I, O, E>( + mut peek: impl FnMut(I) -> IResult<I, O, E> + 'a, +) -> impl FnMut(&mut I) -> PResult<O, E> +where + I: Clone, +{ + move |input| match peek((*input).clone()) { + Ok((i, o)) => { + *input = i; + Ok(o) + } + Err(err) => Err(err), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::binary::be_u16; + use crate::error::ErrMode; + use crate::error::ErrorKind; + use crate::error::InputError; + use crate::error::Needed; + use crate::token::take; + use crate::Partial; + + #[doc(hidden)] + #[macro_export] + macro_rules! assert_size ( + ($t:ty, $sz:expr) => ( + assert!($crate::lib::std::mem::size_of::<$t>() <= $sz, "{} <= {} failed", $crate::lib::std::mem::size_of::<$t>(), $sz); + ); + ); + + #[test] + #[cfg(target_pointer_width = "64")] + fn size_test() { + assert_size!(IResult<&[u8], &[u8], (&[u8], u32)>, 40); + assert_size!(IResult<&str, &str, u32>, 40); + assert_size!(Needed, 8); + assert_size!(ErrMode<u32>, 16); + assert_size!(ErrorKind, 1); + } + + #[test] + fn err_map_test() { + let e = ErrMode::Backtrack(1); + assert_eq!(e.map(|v| v + 1), ErrMode::Backtrack(2)); + } + + #[test] + fn single_element_tuples() { + use crate::ascii::alpha1; + use crate::error::ErrorKind; + + let mut parser = (alpha1,); + assert_eq!(parser.parse_peek("abc123def"), Ok(("123def", ("abc",)))); + assert_eq!( + parser.parse_peek("123def"), + Err(ErrMode::Backtrack(InputError::new( + "123def", + ErrorKind::Slice + ))) + ); + } + + #[test] + fn tuple_test() { + #[allow(clippy::type_complexity)] + fn tuple_3(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (u16, &[u8], &[u8])> { + (be_u16, take(3u8), "fg").parse_peek(i) + } + + assert_eq!( + tuple_3(Partial::new(&b"abcdefgh"[..])), + Ok(( + Partial::new(&b"h"[..]), + (0x6162u16, &b"cde"[..], &b"fg"[..]) + )) + ); + assert_eq!( + tuple_3(Partial::new(&b"abcd"[..])), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + tuple_3(Partial::new(&b"abcde"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + tuple_3(Partial::new(&b"abcdejk"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"jk"[..]), + ErrorKind::Tag + ))) + ); + } + + #[test] + fn unit_type() { + fn parser(i: &mut &str) -> PResult<()> { + ().parse_next(i) + } + assert_eq!(parser.parse_peek("abxsbsh"), Ok(("abxsbsh", ()))); + assert_eq!(parser.parse_peek("sdfjakdsas"), Ok(("sdfjakdsas", ()))); + assert_eq!(parser.parse_peek(""), Ok(("", ()))); + } +} diff --git a/vendor/winnow/src/stream/impls.rs b/vendor/winnow/src/stream/impls.rs new file mode 100644 index 0000000..d76e1bf --- /dev/null +++ b/vendor/winnow/src/stream/impls.rs @@ -0,0 +1,537 @@ +macro_rules! impl_partial_eq { + ($lhs:ty, $rhs:ty) => { + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let l = self.as_ref(); + let r: &Self = other.as_ref(); + PartialEq::eq(l, r) + } + } + + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + PartialEq::eq(other, self) + } + } + }; +} + +macro_rules! impl_partial_ord { + ($lhs:ty, $rhs:ty) => { + impl<'a, 'b> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { + let l = self.as_ref(); + let r: &Self = other.as_ref(); + PartialOrd::partial_cmp(l, r) + } + } + + impl<'a, 'b> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { + PartialOrd::partial_cmp(other, self) + } + } + }; +} + +mod bytes { + use crate::lib::std::{cmp::Ordering, fmt, ops}; + + use crate::stream::Bytes; + + impl fmt::Display for Bytes { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <Self as fmt::UpperHex>::fmt(self, f) + } + } + + impl fmt::Debug for Bytes { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <Self as fmt::UpperHex>::fmt(self, f) + } + } + + impl fmt::LowerHex for Bytes { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for byte in self.as_bytes() { + write!(f, "{:0>2x}", byte)?; + } + Ok(()) + } + } + + impl fmt::UpperHex for Bytes { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (i, byte) in self.as_bytes().iter().enumerate() { + if 0 < i { + let absolute = (self.as_bytes().as_ptr() as usize) + i; + if f.alternate() && absolute != 0 && absolute % 4 == 0 { + write!(f, "_")?; + } + } + write!(f, "{:0>2X}", byte)?; + } + Ok(()) + } + } + + impl ops::Deref for Bytes { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl ops::Index<usize> for Bytes { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.as_bytes()[idx] + } + } + + impl ops::Index<ops::RangeFull> for Bytes { + type Output = Bytes; + + #[inline] + fn index(&self, _: ops::RangeFull) -> &Bytes { + self + } + } + + impl ops::Index<ops::Range<usize>> for Bytes { + type Output = Bytes; + + #[inline] + fn index(&self, r: ops::Range<usize>) -> &Bytes { + Bytes::new(&self.as_bytes()[r.start..r.end]) + } + } + + impl ops::Index<ops::RangeInclusive<usize>> for Bytes { + type Output = Bytes; + + #[inline] + fn index(&self, r: ops::RangeInclusive<usize>) -> &Bytes { + Bytes::new(&self.as_bytes()[*r.start()..=*r.end()]) + } + } + + impl ops::Index<ops::RangeFrom<usize>> for Bytes { + type Output = Bytes; + + #[inline] + fn index(&self, r: ops::RangeFrom<usize>) -> &Bytes { + Bytes::new(&self.as_bytes()[r.start..]) + } + } + + impl ops::Index<ops::RangeTo<usize>> for Bytes { + type Output = Bytes; + + #[inline] + fn index(&self, r: ops::RangeTo<usize>) -> &Bytes { + Bytes::new(&self.as_bytes()[..r.end]) + } + } + + impl ops::Index<ops::RangeToInclusive<usize>> for Bytes { + type Output = Bytes; + + #[inline] + fn index(&self, r: ops::RangeToInclusive<usize>) -> &Bytes { + Bytes::new(&self.as_bytes()[..=r.end]) + } + } + + impl AsRef<[u8]> for Bytes { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl AsRef<Bytes> for [u8] { + #[inline] + fn as_ref(&self) -> &Bytes { + Bytes::new(self) + } + } + + impl AsRef<Bytes> for str { + #[inline] + fn as_ref(&self) -> &Bytes { + Bytes::new(self) + } + } + + #[cfg(feature = "alloc")] + impl crate::lib::std::borrow::ToOwned for Bytes { + type Owned = crate::lib::std::vec::Vec<u8>; + + #[inline] + fn to_owned(&self) -> Self::Owned { + crate::lib::std::vec::Vec::from(self.as_bytes()) + } + } + + #[cfg(feature = "alloc")] + impl crate::lib::std::borrow::Borrow<Bytes> for crate::lib::std::vec::Vec<u8> { + #[inline] + fn borrow(&self) -> &Bytes { + Bytes::from_bytes(self.as_slice()) + } + } + + impl<'a> Default for &'a Bytes { + fn default() -> &'a Bytes { + Bytes::new(b"") + } + } + + impl<'a> From<&'a [u8]> for &'a Bytes { + #[inline] + fn from(s: &'a [u8]) -> &'a Bytes { + Bytes::new(s) + } + } + + impl<'a> From<&'a Bytes> for &'a [u8] { + #[inline] + fn from(s: &'a Bytes) -> &'a [u8] { + Bytes::as_bytes(s) + } + } + + impl<'a> From<&'a str> for &'a Bytes { + #[inline] + fn from(s: &'a str) -> &'a Bytes { + Bytes::new(s.as_bytes()) + } + } + + impl Eq for Bytes {} + + impl PartialEq<Bytes> for Bytes { + #[inline] + fn eq(&self, other: &Bytes) -> bool { + self.as_bytes() == other.as_bytes() + } + } + + impl_partial_eq!(Bytes, [u8]); + impl_partial_eq!(Bytes, &'a [u8]); + impl_partial_eq!(Bytes, str); + impl_partial_eq!(Bytes, &'a str); + + impl PartialOrd for Bytes { + #[inline] + fn partial_cmp(&self, other: &Bytes) -> Option<Ordering> { + Some(self.cmp(other)) + } + } + + impl Ord for Bytes { + #[inline] + fn cmp(&self, other: &Bytes) -> Ordering { + Ord::cmp(self.as_bytes(), other.as_bytes()) + } + } + + impl_partial_ord!(Bytes, [u8]); + impl_partial_ord!(Bytes, &'a [u8]); + impl_partial_ord!(Bytes, str); + impl_partial_ord!(Bytes, &'a str); + + #[cfg(all(test, feature = "std"))] + mod display { + use crate::stream::Bytes; + + #[test] + fn clean() { + assert_eq!(&format!("{}", Bytes::new(b"abc")), "616263"); + assert_eq!(&format!("{}", Bytes::new(b"\xf0\x28\x8c\xbc")), "F0288CBC"); + } + } + + #[cfg(all(test, feature = "std"))] + mod debug { + use crate::stream::Bytes; + + #[test] + fn test_debug() { + assert_eq!( + "000000206674797069736F6D0000020069736F6D69736F32617663316D70", + format!( + "{:?}", + Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp") + ), + ); + } + + #[test] + fn test_pretty_debug() { + // Output can change from run-to-run + format!( + "{:#?}", + Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp") + ); + } + + #[test] + fn test_sliced() { + // Output can change from run-to-run + let total = Bytes::new(b"12345678901234567890"); + format!("{:#?}", total); + format!("{:#?}", &total[1..]); + format!("{:#?}", &total[10..]); + } + } +} + +mod bstr { + use crate::lib::std::{cmp::Ordering, fmt, ops}; + + use crate::stream::BStr; + + #[cfg(feature = "alloc")] + impl fmt::Display for BStr { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + crate::lib::std::string::String::from_utf8_lossy(self.as_bytes()).fmt(f) + } + } + + impl fmt::Debug for BStr { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if !f.alternate() { + write!(f, "\"")?; + } + for byte in self.as_bytes() { + let c = *byte as char; + write!(f, "{}", c.escape_debug())?; + } + if !f.alternate() { + write!(f, "\"")?; + } + Ok(()) + } + } + + impl ops::Deref for BStr { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl ops::Index<usize> for BStr { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.as_bytes()[idx] + } + } + + impl ops::Index<ops::RangeFull> for BStr { + type Output = BStr; + + #[inline] + fn index(&self, _: ops::RangeFull) -> &BStr { + self + } + } + + impl ops::Index<ops::Range<usize>> for BStr { + type Output = BStr; + + #[inline] + fn index(&self, r: ops::Range<usize>) -> &BStr { + BStr::new(&self.as_bytes()[r.start..r.end]) + } + } + + impl ops::Index<ops::RangeInclusive<usize>> for BStr { + type Output = BStr; + + #[inline] + fn index(&self, r: ops::RangeInclusive<usize>) -> &BStr { + BStr::new(&self.as_bytes()[*r.start()..=*r.end()]) + } + } + + impl ops::Index<ops::RangeFrom<usize>> for BStr { + type Output = BStr; + + #[inline] + fn index(&self, r: ops::RangeFrom<usize>) -> &BStr { + BStr::new(&self.as_bytes()[r.start..]) + } + } + + impl ops::Index<ops::RangeTo<usize>> for BStr { + type Output = BStr; + + #[inline] + fn index(&self, r: ops::RangeTo<usize>) -> &BStr { + BStr::new(&self.as_bytes()[..r.end]) + } + } + + impl ops::Index<ops::RangeToInclusive<usize>> for BStr { + type Output = BStr; + + #[inline] + fn index(&self, r: ops::RangeToInclusive<usize>) -> &BStr { + BStr::new(&self.as_bytes()[..=r.end]) + } + } + + impl AsRef<[u8]> for BStr { + #[inline] + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl AsRef<BStr> for [u8] { + #[inline] + fn as_ref(&self) -> &BStr { + BStr::new(self) + } + } + + impl AsRef<BStr> for str { + #[inline] + fn as_ref(&self) -> &BStr { + BStr::new(self) + } + } + + #[cfg(feature = "alloc")] + impl crate::lib::std::borrow::ToOwned for BStr { + type Owned = crate::lib::std::vec::Vec<u8>; + + #[inline] + fn to_owned(&self) -> Self::Owned { + crate::lib::std::vec::Vec::from(self.as_bytes()) + } + } + + #[cfg(feature = "alloc")] + impl crate::lib::std::borrow::Borrow<BStr> for crate::lib::std::vec::Vec<u8> { + #[inline] + fn borrow(&self) -> &BStr { + BStr::from_bytes(self.as_slice()) + } + } + + impl<'a> Default for &'a BStr { + fn default() -> &'a BStr { + BStr::new(b"") + } + } + + impl<'a> From<&'a [u8]> for &'a BStr { + #[inline] + fn from(s: &'a [u8]) -> &'a BStr { + BStr::new(s) + } + } + + impl<'a> From<&'a BStr> for &'a [u8] { + #[inline] + fn from(s: &'a BStr) -> &'a [u8] { + BStr::as_bytes(s) + } + } + + impl<'a> From<&'a str> for &'a BStr { + #[inline] + fn from(s: &'a str) -> &'a BStr { + BStr::new(s.as_bytes()) + } + } + + impl Eq for BStr {} + + impl PartialEq<BStr> for BStr { + #[inline] + fn eq(&self, other: &BStr) -> bool { + self.as_bytes() == other.as_bytes() + } + } + + impl_partial_eq!(BStr, [u8]); + impl_partial_eq!(BStr, &'a [u8]); + impl_partial_eq!(BStr, str); + impl_partial_eq!(BStr, &'a str); + + impl PartialOrd for BStr { + #[inline] + fn partial_cmp(&self, other: &BStr) -> Option<Ordering> { + Some(self.cmp(other)) + } + } + + impl Ord for BStr { + #[inline] + fn cmp(&self, other: &BStr) -> Ordering { + Ord::cmp(self.as_bytes(), other.as_bytes()) + } + } + + impl_partial_ord!(BStr, [u8]); + impl_partial_ord!(BStr, &'a [u8]); + impl_partial_ord!(BStr, str); + impl_partial_ord!(BStr, &'a str); + + #[cfg(all(test, feature = "std"))] + mod display { + use crate::stream::BStr; + + #[test] + fn clean() { + assert_eq!(&format!("{}", BStr::new(b"abc")), "abc"); + assert_eq!(&format!("{}", BStr::new(b"\xf0\x28\x8c\xbc")), "�(��"); + } + } + + #[cfg(all(test, feature = "std"))] + mod debug { + use crate::stream::BStr; + + #[test] + fn test_debug() { + assert_eq!(&format!("{:?}", BStr::new(b"abc")), "\"abc\""); + + assert_eq!( + "\"\\0\\0\\0 ftypisom\\0\\0\\u{2}\\0isomiso2avc1mp\"", + format!( + "{:?}", + BStr::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp") + ), + ); + } + + #[test] + fn test_pretty_debug() { + assert_eq!(&format!("{:#?}", BStr::new(b"abc")), "abc"); + } + } +} diff --git a/vendor/winnow/src/stream/mod.rs b/vendor/winnow/src/stream/mod.rs new file mode 100644 index 0000000..f4c31e5 --- /dev/null +++ b/vendor/winnow/src/stream/mod.rs @@ -0,0 +1,2740 @@ +//! Stream capability for combinators to parse +//! +//! 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 +//! [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 +//! - [Custom stream types][crate::_topic::stream] + +use core::num::NonZeroUsize; + +use crate::error::Needed; +use crate::lib::std::iter::{Cloned, Enumerate}; +use crate::lib::std::slice::Iter; +use crate::lib::std::str::from_utf8; +use crate::lib::std::str::CharIndices; +use crate::lib::std::str::FromStr; + +#[allow(unused_imports)] +#[cfg(feature = "unstable-doc")] +use crate::error::ErrMode; + +#[cfg(feature = "alloc")] +use crate::lib::std::collections::BTreeMap; +#[cfg(feature = "std")] +use crate::lib::std::collections::HashMap; +#[cfg(feature = "alloc")] +use crate::lib::std::string::String; +#[cfg(feature = "alloc")] +use crate::lib::std::vec::Vec; + +mod impls; +#[cfg(test)] +mod tests; + +/// UTF-8 Stream +pub type Str<'i> = &'i str; + +/// Improved `Debug` experience for `&[u8]` byte streams +#[allow(clippy::derive_hash_xor_eq)] +#[derive(Hash)] +#[repr(transparent)] +pub struct Bytes([u8]); + +impl Bytes { + /// Make a stream out of a byte slice-like. + #[inline] + pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self { + Self::from_bytes(bytes.as_ref()) + } + + #[inline] + fn from_bytes(slice: &[u8]) -> &Self { + unsafe { crate::lib::std::mem::transmute(slice) } + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +/// Improved `Debug` experience for `&[u8]` UTF-8-ish streams +#[allow(clippy::derive_hash_xor_eq)] +#[derive(Hash)] +#[repr(transparent)] +pub struct BStr([u8]); + +impl BStr { + /// Make a stream out of a byte slice-like. + #[inline] + pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self { + Self::from_bytes(bytes.as_ref()) + } + + #[inline] + fn from_bytes(slice: &[u8]) -> &Self { + unsafe { crate::lib::std::mem::transmute(slice) } + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +/// Allow collecting the span of a parsed token +/// +/// Spans are tracked as a [`Range<usize>`] of byte offsets. +/// +/// Converting byte offsets to line or column numbers is left up to the user, as computing column +/// numbers requires domain knowledge (are columns byte-based, codepoint-based, or grapheme-based?) +/// and O(n) iteration over the input to determine codepoint and line boundaries. +/// +/// [The `line-span` crate](https://docs.rs/line-span/latest/line_span/) can help with converting +/// 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)] +pub struct Located<I> { + initial: I, + input: I, +} + +impl<I> Located<I> +where + I: Clone + Offset, +{ + /// Wrap another Stream with span tracking + pub fn new(input: I) -> Self { + let initial = input.clone(); + Self { initial, input } + } + + fn location(&self) -> usize { + self.input.offset_from(&self.initial) + } +} + +impl<I> AsRef<I> for Located<I> { + #[inline(always)] + fn as_ref(&self) -> &I { + &self.input + } +} + +impl<I> crate::lib::std::ops::Deref for Located<I> { + type Target = I; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.input + } +} + +impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for Located<I> { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + self.input.fmt(f) + } +} + +/// Thread global state through your parsers +/// +/// Use cases +/// - Recursion checks +/// - Error recovery +/// - Debugging +/// +/// # Example +/// +/// ``` +/// # use std::cell::Cell; +/// # use winnow::prelude::*; +/// # use winnow::stream::Stateful; +/// # use winnow::ascii::alpha1; +/// # type Error = (); +/// +/// #[derive(Clone, Debug)] +/// struct State<'s>(&'s Cell<u32>); +/// +/// impl<'s> State<'s> { +/// fn count(&self) { +/// self.0.set(self.0.get() + 1); +/// } +/// } +/// +/// type Stream<'is> = Stateful<&'is str, State<'is>>; +/// +/// fn word<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { +/// i.state.count(); +/// alpha1.parse_next(i) +/// } +/// +/// let data = "Hello"; +/// let state = Cell::new(0); +/// let input = Stream { input: data, state: State(&state) }; +/// let output = word.parse(input).unwrap(); +/// assert_eq!(state.get(), 1); +/// ``` +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Stateful<I, S> { + /// Inner input being wrapped in state + pub input: I, + /// User-provided state + pub state: S, +} + +impl<I, S> AsRef<I> for Stateful<I, S> { + #[inline(always)] + fn as_ref(&self) -> &I { + &self.input + } +} + +impl<I, S> crate::lib::std::ops::Deref for Stateful<I, S> { + type Target = I; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl<I: crate::lib::std::fmt::Display, S> crate::lib::std::fmt::Display for Stateful<I, S> { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + self.input.fmt(f) + } +} + +/// 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 +/// small files that can be read entirely to memory. +/// +/// In contrast, streaming input assumes that we might not have all of the data. +/// This can happen with some network protocol or large file parsers, where the +/// input buffer can be full and need to be resized or refilled. +/// - [`ErrMode::Incomplete`] will report how much more data is needed. +/// - [`Parser::complete_err`][crate::Parser::complete_err] transform [`ErrMode::Incomplete`] to +/// [`ErrMode::Backtrack`] +/// +/// See also [`StreamIsPartial`] to tell whether the input supports complete or partial parsing. +/// +/// See also [Special Topics: Parsing Partial Input][crate::_topic::partial]. +/// +/// # Example +/// +/// Here is how it works in practice: +/// +/// ```rust +/// # use winnow::{PResult, error::ErrMode, error::Needed, error::{InputError, ErrorKind}, token, ascii, stream::Partial}; +/// # use winnow::prelude::*; +/// +/// fn take_partial<'s>(i: &mut Partial<&'s [u8]>) -> PResult<&'s [u8], InputError<Partial<&'s [u8]>>> { +/// token::take(4u8).parse_next(i) +/// } +/// +/// fn take_complete<'s>(i: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { +/// token::take(4u8).parse_next(i) +/// } +/// +/// // both parsers will take 4 bytes as expected +/// assert_eq!(take_partial.parse_peek(Partial::new(&b"abcde"[..])), Ok((Partial::new(&b"e"[..]), &b"abcd"[..]))); +/// assert_eq!(take_complete.parse_peek(&b"abcde"[..]), Ok((&b"e"[..], &b"abcd"[..]))); +/// +/// // if the input is smaller than 4 bytes, the partial parser +/// // will return `Incomplete` to indicate that we need more data +/// assert_eq!(take_partial.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); +/// +/// // 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 +/// fn alpha0_partial<'s>(i: &mut Partial<&'s str>) -> PResult<&'s str, InputError<Partial<&'s str>>> { +/// ascii::alpha0.parse_next(i) +/// } +/// +/// fn alpha0_complete<'s>(i: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// ascii::alpha0.parse_next(i) +/// } +/// +/// // if there's a clear limit to the recognized 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 +/// // "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)] +pub struct Partial<I> { + input: I, + partial: bool, +} + +impl<I> Partial<I> +where + I: StreamIsPartial, +{ + /// Create a partial input + pub fn new(input: I) -> Self { + debug_assert!( + !I::is_partial_supported(), + "`Partial` can only wrap complete sources" + ); + let partial = true; + Self { input, partial } + } + + /// Extract the original [`Stream`] + #[inline(always)] + pub fn into_inner(self) -> I { + self.input + } +} + +impl<I> Default for Partial<I> +where + I: Default + StreamIsPartial, +{ + fn default() -> Self { + Self::new(I::default()) + } +} + +impl<I> crate::lib::std::ops::Deref for Partial<I> { + type Target = I; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.input + } +} + +impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for Partial<I> { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + self.input.fmt(f) + } +} + +/// Abstract method to calculate the input length +pub trait SliceLen { + /// Calculates the input length, as indicated by its name, + /// and the name of the trait itself + fn slice_len(&self) -> usize; +} + +impl<'a, T> SliceLen for &'a [T] { + #[inline] + fn slice_len(&self) -> usize { + self.len() + } +} + +impl<T, const LEN: usize> SliceLen for [T; LEN] { + #[inline] + fn slice_len(&self) -> usize { + self.len() + } +} + +impl<'a, T, const LEN: usize> SliceLen for &'a [T; LEN] { + #[inline] + fn slice_len(&self) -> usize { + self.len() + } +} + +impl<'a> SliceLen for &'a str { + #[inline] + fn slice_len(&self) -> usize { + self.len() + } +} + +impl<'a> SliceLen for &'a Bytes { + #[inline] + fn slice_len(&self) -> usize { + self.len() + } +} + +impl<'a> SliceLen for &'a BStr { + #[inline] + fn slice_len(&self) -> usize { + self.len() + } +} + +impl<I> SliceLen for (I, usize, usize) +where + I: SliceLen, +{ + #[inline(always)] + fn slice_len(&self) -> usize { + self.0.slice_len() * 8 + self.2 - self.1 + } +} + +impl<I> SliceLen for Located<I> +where + I: SliceLen, +{ + #[inline(always)] + fn slice_len(&self) -> usize { + self.input.slice_len() + } +} + +impl<I, S> SliceLen for Stateful<I, S> +where + I: SliceLen, +{ + #[inline(always)] + fn slice_len(&self) -> usize { + self.input.slice_len() + } +} + +impl<I> SliceLen for Partial<I> +where + I: SliceLen, +{ + #[inline(always)] + fn slice_len(&self) -> usize { + self.input.slice_len() + } +} + +/// Core definition for parser input state +pub trait Stream: Offset<<Self as Stream>::Checkpoint> + crate::lib::std::fmt::Debug { + /// The smallest unit being parsed + /// + /// Example: `u8` for `&[u8]` or `char` for `&str` + type Token: crate::lib::std::fmt::Debug; + /// Sequence of `Token`s + /// + /// Example: `&[u8]` for `Located<&[u8]>` or `&str` for `Located<&str>` + type Slice: crate::lib::std::fmt::Debug; + + /// Iterate with the offset from the current location + type IterOffsets: Iterator<Item = (usize, Self::Token)>; + + /// A parse location within the stream + type Checkpoint: Offset + Clone + crate::lib::std::fmt::Debug; + + /// Iterate with the offset from the current location + fn iter_offsets(&self) -> Self::IterOffsets; + + /// Returns the offset to the end of the input + fn eof_offset(&self) -> usize; + + /// Split off the next token from the input + fn next_token(&mut self) -> Option<Self::Token>; + /// Split off the next token from the input + #[inline(always)] + fn peek_token(&self) -> Option<(Self, Self::Token)> + where + Self: Clone, + { + let mut peek = self.clone(); + let token = peek.next_token()?; + Some((peek, token)) + } + + /// Finds the offset of the next matching token + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool; + /// Get the offset for the number of `tokens` into the stream + /// + /// This means "0 tokens" will return `0` offset + 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 + /// with the number of tokens. To get a valid offset, use: + /// - [`Stream::eof_offset`] + /// - [`Stream::iter_offsets`] + /// - [`Stream::offset_for`] + /// - [`Stream::offset_at`] + /// + /// # Panic + /// + /// This will panic if + /// + /// * Indexes must be within bounds of the original input; + /// * Indexes must uphold invariants of the stream, like for `str` they must lie on UTF-8 + /// sequence boundaries. + /// + fn next_slice(&mut self, offset: usize) -> Self::Slice; + /// Split off a slice of tokens from the input + #[inline(always)] + fn peek_slice(&self, offset: usize) -> (Self, Self::Slice) + where + Self: Clone, + { + let mut peek = self.clone(); + let slice = peek.next_slice(offset); + (peek, slice) + } + + /// Advance to the end of the stream + #[inline(always)] + fn finish(&mut self) -> Self::Slice { + self.next_slice(self.eof_offset()) + } + /// Advance to the end of the stream + #[inline(always)] + fn peek_finish(&self) -> (Self, Self::Slice) + where + Self: Clone, + { + let mut peek = self.clone(); + let slice = peek.finish(); + (peek, slice) + } + + /// Save the current parse location within the stream + fn checkpoint(&self) -> Self::Checkpoint; + /// Revert the stream to a prior [`Self::Checkpoint`] + /// + /// # Panic + /// + /// May panic if an invalid [`Self::Checkpoint`] is provided + fn reset(&mut self, checkpoint: Self::Checkpoint); + + /// Return the inner-most stream + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug; +} + +impl<'i, T> Stream for &'i [T] +where + T: Clone + crate::lib::std::fmt::Debug, +{ + type Token = T; + type Slice = &'i [T]; + + type IterOffsets = Enumerate<Cloned<Iter<'i, T>>>; + + type Checkpoint = Checkpoint<Self>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.iter().cloned().enumerate() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.len() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + let (token, next) = self.split_first()?; + *self = next; + Some(token.clone()) + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.iter().position(|b| predicate(b.clone())) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) { + Err(Needed::Size(needed)) + } else { + Ok(tokens) + } + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + let (slice, next) = self.split_at(offset); + *self = next; + slice + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(*self) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + *self = checkpoint.0; + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + self + } +} + +impl<'i> Stream for &'i str { + type Token = char; + type Slice = &'i str; + + type IterOffsets = CharIndices<'i>; + + type Checkpoint = Checkpoint<Self>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.char_indices() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.len() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + let c = self.chars().next()?; + let offset = c.len(); + *self = &self[offset..]; + Some(c) + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + for (o, c) in self.iter_offsets() { + if predicate(c) { + return Some(o); + } + } + None + } + #[inline] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + let mut cnt = 0; + for (offset, _) in self.iter_offsets() { + if cnt == tokens { + return Ok(offset); + } + cnt += 1; + } + + if cnt == tokens { + Ok(self.eof_offset()) + } else { + Err(Needed::Unknown) + } + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + let (slice, next) = self.split_at(offset); + *self = next; + slice + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(*self) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + *self = checkpoint.0; + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + self + } +} + +impl<'i> Stream for &'i Bytes { + type Token = u8; + type Slice = &'i [u8]; + + type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>; + + type Checkpoint = Checkpoint<Self>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.iter().cloned().enumerate() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.len() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + if self.is_empty() { + None + } else { + let token = self[0]; + *self = &self[1..]; + Some(token) + } + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.iter().position(|b| predicate(*b)) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) { + Err(Needed::Size(needed)) + } else { + Ok(tokens) + } + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + let (slice, next) = self.0.split_at(offset); + *self = Bytes::from_bytes(next); + slice + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(*self) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + *self = checkpoint.0; + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + self + } +} + +impl<'i> Stream for &'i BStr { + type Token = u8; + type Slice = &'i [u8]; + + type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>; + + type Checkpoint = Checkpoint<Self>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.iter().cloned().enumerate() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.len() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + if self.is_empty() { + None + } else { + let token = self[0]; + *self = &self[1..]; + Some(token) + } + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.iter().position(|b| predicate(*b)) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) { + Err(Needed::Size(needed)) + } else { + Ok(tokens) + } + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + let (slice, next) = self.0.split_at(offset); + *self = BStr::from_bytes(next); + slice + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(*self) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + *self = checkpoint.0; + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + self + } +} + +impl<I> Stream for (I, usize) +where + I: Stream<Token = u8> + Clone, +{ + type Token = bool; + type Slice = (I::Slice, usize, usize); + + type IterOffsets = BitOffsets<I>; + + type Checkpoint = Checkpoint<(I::Checkpoint, usize)>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + BitOffsets { + i: self.clone(), + o: 0, + } + } + #[inline(always)] + fn eof_offset(&self) -> usize { + let offset = self.0.eof_offset() * 8; + if offset == 0 { + 0 + } else { + offset - self.1 + } + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + next_bit(self) + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.iter_offsets() + .find_map(|(o, b)| predicate(b).then_some(o)) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + if let Some(needed) = tokens + .checked_sub(self.eof_offset()) + .and_then(NonZeroUsize::new) + { + Err(Needed::Size(needed)) + } else { + Ok(tokens) + } + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + let byte_offset = (offset + self.1) / 8; + let end_offset = (offset + self.1) % 8; + let s = self.0.next_slice(byte_offset); + let start_offset = self.1; + self.1 = end_offset; + (s, start_offset, end_offset) + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint((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; + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + &self.0 + } +} + +/// Iterator for [bit][crate::binary::bits] stream (`(I, usize)`) +pub struct BitOffsets<I> { + i: (I, usize), + o: usize, +} + +impl<I> Iterator for BitOffsets<I> +where + I: Stream<Token = u8> + Clone, +{ + type Item = (usize, bool); + fn next(&mut self) -> Option<Self::Item> { + let b = next_bit(&mut self.i)?; + let o = self.o; + + self.o += 1; + + Some((o, b)) + } +} + +fn next_bit<I>(i: &mut (I, usize)) -> Option<bool> +where + I: Stream<Token = u8> + Clone, +{ + if i.eof_offset() == 0 { + return None; + } + let offset = i.1; + + let mut next_i = i.0.clone(); + let byte = next_i.next_token()?; + let bit = (byte >> offset) & 0x1 == 0x1; + + let next_offset = offset + 1; + if next_offset == 8 { + i.0 = next_i; + i.1 = 0; + Some(bit) + } else { + i.1 = next_offset; + Some(bit) + } +} + +impl<I: Stream> Stream for Located<I> { + type Token = <I as Stream>::Token; + type Slice = <I as Stream>::Slice; + + type IterOffsets = <I as Stream>::IterOffsets; + + type Checkpoint = Checkpoint<I::Checkpoint>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.input.iter_offsets() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.input.eof_offset() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + self.input.next_token() + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.input.offset_for(predicate) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + self.input.offset_at(tokens) + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + self.input.next_slice(offset) + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(self.input.checkpoint()) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + self.input.reset(checkpoint.0); + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + &self.input + } +} + +impl<I: Stream, S: Clone + crate::lib::std::fmt::Debug> Stream for Stateful<I, S> { + type Token = <I as Stream>::Token; + type Slice = <I as Stream>::Slice; + + type IterOffsets = <I as Stream>::IterOffsets; + + type Checkpoint = Checkpoint<I::Checkpoint>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.input.iter_offsets() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.input.eof_offset() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + self.input.next_token() + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.input.offset_for(predicate) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + self.input.offset_at(tokens) + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + self.input.next_slice(offset) + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(self.input.checkpoint()) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + self.input.reset(checkpoint.0); + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + &self.input + } +} + +impl<I: Stream> Stream for Partial<I> { + type Token = <I as Stream>::Token; + type Slice = <I as Stream>::Slice; + + type IterOffsets = <I as Stream>::IterOffsets; + + type Checkpoint = Checkpoint<I::Checkpoint>; + + #[inline(always)] + fn iter_offsets(&self) -> Self::IterOffsets { + self.input.iter_offsets() + } + #[inline(always)] + fn eof_offset(&self) -> usize { + self.input.eof_offset() + } + + #[inline(always)] + fn next_token(&mut self) -> Option<Self::Token> { + self.input.next_token() + } + + #[inline(always)] + fn offset_for<P>(&self, predicate: P) -> Option<usize> + where + P: Fn(Self::Token) -> bool, + { + self.input.offset_for(predicate) + } + #[inline(always)] + fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { + self.input.offset_at(tokens) + } + #[inline(always)] + fn next_slice(&mut self, offset: usize) -> Self::Slice { + self.input.next_slice(offset) + } + + #[inline(always)] + fn checkpoint(&self) -> Self::Checkpoint { + Checkpoint(self.input.checkpoint()) + } + #[inline(always)] + fn reset(&mut self, checkpoint: Self::Checkpoint) { + self.input.reset(checkpoint.0); + } + + #[inline(always)] + fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { + &self.input + } +} + +/// Number of indices input has advanced since start of parsing +pub trait Location { + /// Number of indices input has advanced since start of parsing + fn location(&self) -> usize; +} + +impl<I> Location for Located<I> +where + I: Clone + Offset, +{ + #[inline(always)] + fn location(&self) -> usize { + self.location() + } +} + +impl<I, S> Location for Stateful<I, S> +where + I: Location, +{ + #[inline(always)] + fn location(&self) -> usize { + self.input.location() + } +} + +impl<I> Location for Partial<I> +where + I: Location, +{ + #[inline(always)] + fn location(&self) -> usize { + self.input.location() + } +} + +/// Marks the input as being the complete buffer or a partial buffer for streaming input +/// +/// See [`Partial`] for marking a presumed complete buffer type as a streaming buffer. +pub trait StreamIsPartial: Sized { + /// Whether the stream is currently partial or complete + type PartialState; + + /// Mark the stream is complete + #[must_use] + fn complete(&mut self) -> Self::PartialState; + + /// Restore the stream back to its previous state + fn restore_partial(&mut self, state: Self::PartialState); + + /// Report whether the [`Stream`] is can ever be incomplete + fn is_partial_supported() -> bool; + + /// Report whether the [`Stream`] is currently incomplete + #[inline(always)] + fn is_partial(&self) -> bool { + Self::is_partial_supported() + } +} + +impl<'a, T> StreamIsPartial for &'a [T] { + type PartialState = (); + + fn complete(&mut self) -> Self::PartialState {} + + fn restore_partial(&mut self, _state: Self::PartialState) {} + + #[inline(always)] + fn is_partial_supported() -> bool { + false + } +} + +impl<'a> StreamIsPartial for &'a str { + type PartialState = (); + + fn complete(&mut self) -> Self::PartialState { + // Already complete + } + + fn restore_partial(&mut self, _state: Self::PartialState) {} + + #[inline(always)] + fn is_partial_supported() -> bool { + false + } +} + +impl<'a> StreamIsPartial for &'a Bytes { + type PartialState = (); + + fn complete(&mut self) -> Self::PartialState { + // Already complete + } + + fn restore_partial(&mut self, _state: Self::PartialState) {} + + #[inline(always)] + fn is_partial_supported() -> bool { + false + } +} + +impl<'a> StreamIsPartial for &'a BStr { + type PartialState = (); + + fn complete(&mut self) -> Self::PartialState { + // Already complete + } + + fn restore_partial(&mut self, _state: Self::PartialState) {} + + #[inline(always)] + fn is_partial_supported() -> bool { + false + } +} + +impl<I> StreamIsPartial for (I, usize) +where + I: StreamIsPartial, +{ + type PartialState = I::PartialState; + + fn complete(&mut self) -> Self::PartialState { + self.0.complete() + } + + fn restore_partial(&mut self, state: Self::PartialState) { + self.0.restore_partial(state); + } + + #[inline(always)] + fn is_partial_supported() -> bool { + I::is_partial_supported() + } + + #[inline(always)] + fn is_partial(&self) -> bool { + self.0.is_partial() + } +} + +impl<I> StreamIsPartial for Located<I> +where + I: StreamIsPartial, +{ + type PartialState = I::PartialState; + + fn complete(&mut self) -> Self::PartialState { + self.input.complete() + } + + fn restore_partial(&mut self, state: Self::PartialState) { + self.input.restore_partial(state); + } + + #[inline(always)] + fn is_partial_supported() -> bool { + I::is_partial_supported() + } + + #[inline(always)] + fn is_partial(&self) -> bool { + self.input.is_partial() + } +} + +impl<I, S> StreamIsPartial for Stateful<I, S> +where + I: StreamIsPartial, +{ + type PartialState = I::PartialState; + + fn complete(&mut self) -> Self::PartialState { + self.input.complete() + } + + fn restore_partial(&mut self, state: Self::PartialState) { + self.input.restore_partial(state); + } + + #[inline(always)] + fn is_partial_supported() -> bool { + I::is_partial_supported() + } + + #[inline(always)] + fn is_partial(&self) -> bool { + self.input.is_partial() + } +} + +impl<I> StreamIsPartial for Partial<I> +where + I: StreamIsPartial, +{ + type PartialState = bool; + + fn complete(&mut self) -> Self::PartialState { + core::mem::replace(&mut self.partial, false) + } + + fn restore_partial(&mut self, state: Self::PartialState) { + self.partial = state; + } + + #[inline(always)] + fn is_partial_supported() -> bool { + true + } + + #[inline(always)] + fn is_partial(&self) -> bool { + self.partial + } +} + +/// Useful functions to calculate the offset between slices and show a hexdump of a slice +pub trait Offset<Start = Self> { + /// Offset between the first byte of `start` and the first byte of `self`a + /// + /// **Note:** This is an offset, not an index, and may point to the end of input + /// (`start.len()`) when `self` is exhausted. + fn offset_from(&self, start: &Start) -> usize; +} + +impl<'a, T> Offset for &'a [T] { + #[inline] + fn offset_from(&self, start: &Self) -> usize { + let fst = (*start).as_ptr(); + let snd = (*self).as_ptr(); + + debug_assert!( + fst <= snd, + "`Offset::offset_to` only accepts slices of `self`" + ); + (snd as usize - fst as usize) / crate::lib::std::mem::size_of::<T>() + } +} + +impl<'a, T> Offset<<&'a [T] as Stream>::Checkpoint> for &'a [T] +where + T: Clone + crate::lib::std::fmt::Debug, +{ + #[inline(always)] + fn offset_from(&self, other: &<&'a [T] as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<'a> Offset for &'a str { + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.as_bytes().offset_from(&start.as_bytes()) + } +} + +impl<'a> Offset<<&'a str as Stream>::Checkpoint> for &'a str { + #[inline(always)] + fn offset_from(&self, other: &<&'a str as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<'a> Offset for &'a Bytes { + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.as_bytes().offset_from(&start.as_bytes()) + } +} + +impl<'a> Offset<<&'a Bytes as Stream>::Checkpoint> for &'a Bytes { + #[inline(always)] + fn offset_from(&self, other: &<&'a Bytes as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<'a> Offset for &'a BStr { + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.as_bytes().offset_from(&start.as_bytes()) + } +} + +impl<'a> Offset<<&'a BStr as Stream>::Checkpoint> for &'a BStr { + #[inline(always)] + fn offset_from(&self, other: &<&'a BStr as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<I> Offset for (I, usize) +where + I: Offset, +{ + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.0.offset_from(&start.0) * 8 + self.1 - start.1 + } +} + +impl<I> Offset<<(I, usize) as Stream>::Checkpoint> for (I, usize) +where + I: Stream<Token = u8> + Clone, +{ + #[inline(always)] + fn offset_from(&self, other: &<(I, usize) as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<I> Offset for Located<I> +where + I: Stream, +{ + #[inline(always)] + fn offset_from(&self, other: &Self) -> usize { + self.offset_from(&other.checkpoint()) + } +} + +impl<I> Offset<<Located<I> as Stream>::Checkpoint> for Located<I> +where + I: Stream, +{ + #[inline(always)] + fn offset_from(&self, other: &<Located<I> as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<I, S> Offset for Stateful<I, S> +where + I: Stream, + S: Clone + crate::lib::std::fmt::Debug, +{ + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.offset_from(&start.checkpoint()) + } +} + +impl<I, S> Offset<<Stateful<I, S> as Stream>::Checkpoint> for Stateful<I, S> +where + I: Stream, + S: Clone + crate::lib::std::fmt::Debug, +{ + #[inline(always)] + fn offset_from(&self, other: &<Stateful<I, S> as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<I> Offset for Partial<I> +where + I: Stream, +{ + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.offset_from(&start.checkpoint()) + } +} + +impl<I> Offset<<Partial<I> as Stream>::Checkpoint> for Partial<I> +where + I: Stream, +{ + #[inline(always)] + fn offset_from(&self, other: &<Partial<I> as Stream>::Checkpoint) -> usize { + self.checkpoint().offset_from(other) + } +} + +impl<I> Offset for Checkpoint<I> +where + I: Offset, +{ + #[inline(always)] + fn offset_from(&self, start: &Self) -> usize { + self.0.offset_from(&start.0) + } +} + +/// Helper trait for types that can be viewed as a byte slice +pub trait AsBytes { + /// Casts the input type to a byte slice + fn as_bytes(&self) -> &[u8]; +} + +impl<'a> AsBytes for &'a [u8] { + #[inline(always)] + fn as_bytes(&self) -> &[u8] { + self + } +} + +impl<'a> AsBytes for &'a Bytes { + #[inline(always)] + fn as_bytes(&self) -> &[u8] { + (*self).as_bytes() + } +} + +impl<I> AsBytes for Located<I> +where + I: AsBytes, +{ + #[inline(always)] + fn as_bytes(&self) -> &[u8] { + self.input.as_bytes() + } +} + +impl<I, S> AsBytes for Stateful<I, S> +where + I: AsBytes, +{ + #[inline(always)] + fn as_bytes(&self) -> &[u8] { + self.input.as_bytes() + } +} + +impl<I> AsBytes for Partial<I> +where + I: AsBytes, +{ + #[inline(always)] + fn as_bytes(&self) -> &[u8] { + self.input.as_bytes() + } +} + +/// Helper trait for types that can be viewed as a byte slice +pub trait AsBStr { + /// Casts the input type to a byte slice + fn as_bstr(&self) -> &[u8]; +} + +impl<'a> AsBStr for &'a [u8] { + #[inline(always)] + fn as_bstr(&self) -> &[u8] { + self + } +} + +impl<'a> AsBStr for &'a BStr { + #[inline(always)] + fn as_bstr(&self) -> &[u8] { + (*self).as_bytes() + } +} + +impl<'a> AsBStr for &'a str { + #[inline(always)] + fn as_bstr(&self) -> &[u8] { + (*self).as_bytes() + } +} + +impl<I> AsBStr for Located<I> +where + I: AsBStr, +{ + #[inline(always)] + fn as_bstr(&self) -> &[u8] { + self.input.as_bstr() + } +} + +impl<I, S> AsBStr for Stateful<I, S> +where + I: AsBStr, +{ + #[inline(always)] + fn as_bstr(&self) -> &[u8] { + self.input.as_bstr() + } +} + +impl<I> AsBStr for Partial<I> +where + I: AsBStr, +{ + #[inline(always)] + fn as_bstr(&self) -> &[u8] { + self.input.as_bstr() + } +} + +/// Result of [`Compare::compare`] +#[derive(Debug, Eq, PartialEq)] +pub enum CompareResult { + /// Comparison was successful + Ok, + /// We need more data to be sure + Incomplete, + /// Comparison failed + Error, +} + +/// Abstracts comparison operations +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 + fn compare_no_case(&self, t: T) -> CompareResult; +} + +fn lowercase_byte(c: u8) -> u8 { + match c { + b'A'..=b'Z' => c - b'A' + b'a', + _ => c, + } +} + +impl<'a, 'b> Compare<&'b [u8]> for &'a [u8] { + #[inline] + fn compare(&self, t: &'b [u8]) -> CompareResult { + let pos = self.iter().zip(t.iter()).position(|(a, b)| a != b); + + match pos { + Some(_) => CompareResult::Error, + None => { + if self.len() >= t.len() { + CompareResult::Ok + } else { + CompareResult::Incomplete + } + } + } + } + + #[inline] + fn compare_no_case(&self, t: &'b [u8]) -> CompareResult { + if self + .iter() + .zip(t) + .any(|(a, b)| lowercase_byte(*a) != lowercase_byte(*b)) + { + CompareResult::Error + } else if self.len() < t.len() { + CompareResult::Incomplete + } else { + CompareResult::Ok + } + } +} + +impl<'a, const LEN: usize> Compare<[u8; LEN]> for &'a [u8] { + #[inline(always)] + fn compare(&self, t: [u8; LEN]) -> CompareResult { + self.compare(&t[..]) + } + + #[inline(always)] + fn compare_no_case(&self, t: [u8; LEN]) -> CompareResult { + self.compare_no_case(&t[..]) + } +} + +impl<'a, 'b, const LEN: usize> Compare<&'b [u8; LEN]> for &'a [u8] { + #[inline(always)] + fn compare(&self, t: &'b [u8; LEN]) -> CompareResult { + self.compare(&t[..]) + } + + #[inline(always)] + fn compare_no_case(&self, t: &'b [u8; LEN]) -> CompareResult { + self.compare_no_case(&t[..]) + } +} + +impl<'a, 'b> Compare<&'b str> for &'a [u8] { + #[inline(always)] + fn compare(&self, t: &'b str) -> CompareResult { + self.compare(t.as_bytes()) + } + #[inline(always)] + fn compare_no_case(&self, t: &'b str) -> CompareResult { + self.compare_no_case(t.as_bytes()) + } +} + +impl<'a, 'b> Compare<&'b str> for &'a str { + #[inline(always)] + fn compare(&self, t: &'b str) -> CompareResult { + self.as_bytes().compare(t.as_bytes()) + } + + //FIXME: this version is too simple and does not use the current locale + #[inline] + fn compare_no_case(&self, t: &'b str) -> CompareResult { + let pos = self + .chars() + .zip(t.chars()) + .position(|(a, b)| a.to_lowercase().ne(b.to_lowercase())); + + match pos { + Some(_) => CompareResult::Error, + None => { + if self.len() >= t.len() { + CompareResult::Ok + } else { + CompareResult::Incomplete + } + } + } + } +} + +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)] + fn compare_no_case(&self, t: &'b [u8]) -> CompareResult { + AsBStr::as_bstr(self).compare_no_case(t) + } +} + +impl<'a, T> Compare<T> for &'a Bytes +where + &'a [u8]: Compare<T>, +{ + #[inline(always)] + fn compare(&self, t: T) -> CompareResult { + let bytes = (*self).as_bytes(); + bytes.compare(t) + } + + #[inline(always)] + 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 +where + &'a [u8]: Compare<T>, +{ + #[inline(always)] + fn compare(&self, t: T) -> CompareResult { + let bytes = (*self).as_bytes(); + bytes.compare(t) + } + + #[inline(always)] + 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> +where + I: Compare<U>, +{ + #[inline(always)] + fn compare(&self, other: U) -> CompareResult { + self.input.compare(other) + } + + #[inline(always)] + fn compare_no_case(&self, other: U) -> CompareResult { + self.input.compare_no_case(other) + } +} + +impl<I, S, U> Compare<U> for Stateful<I, S> +where + I: Compare<U>, +{ + #[inline(always)] + fn compare(&self, other: U) -> CompareResult { + self.input.compare(other) + } + + #[inline(always)] + fn compare_no_case(&self, other: U) -> CompareResult { + self.input.compare_no_case(other) + } +} + +impl<I, T> Compare<T> for Partial<I> +where + I: Compare<T>, +{ + #[inline(always)] + fn compare(&self, t: T) -> CompareResult { + self.input.compare(t) + } + + #[inline(always)] + 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>; +} + +impl<'i, 's> FindSlice<&'s [u8]> for &'i [u8] { + #[inline(always)] + fn find_slice(&self, substr: &'s [u8]) -> Option<usize> { + memmem(self, substr) + } +} + +impl<'i> FindSlice<u8> for &'i [u8] { + #[inline(always)] + fn find_slice(&self, substr: u8) -> Option<usize> { + memchr(substr, self) + } +} + +impl<'i, 's> FindSlice<&'s str> for &'i [u8] { + #[inline(always)] + fn find_slice(&self, substr: &'s str) -> Option<usize> { + self.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> { + self.find(substr) + } +} + +impl<'i> FindSlice<char> for &'i str { + #[inline(always)] + fn find_slice(&self, substr: char) -> Option<usize> { + self.find(substr) + } +} + +impl<'i, S> FindSlice<S> for &'i Bytes +where + &'i [u8]: FindSlice<S>, +{ + #[inline(always)] + fn find_slice(&self, substr: S) -> Option<usize> { + let bytes = (*self).as_bytes(); + let offset = bytes.find_slice(substr); + offset + } +} + +impl<'i, S> FindSlice<S> for &'i BStr +where + &'i [u8]: FindSlice<S>, +{ + #[inline(always)] + fn find_slice(&self, substr: S) -> Option<usize> { + let bytes = (*self).as_bytes(); + let offset = bytes.find_slice(substr); + offset + } +} + +impl<I, T> FindSlice<T> for Located<I> +where + I: FindSlice<T>, +{ + #[inline(always)] + fn find_slice(&self, substr: T) -> Option<usize> { + self.input.find_slice(substr) + } +} + +impl<I, S, T> FindSlice<T> for Stateful<I, S> +where + I: FindSlice<T>, +{ + #[inline(always)] + fn find_slice(&self, substr: T) -> Option<usize> { + self.input.find_slice(substr) + } +} + +impl<I, T> FindSlice<T> for Partial<I> +where + I: FindSlice<T>, +{ + #[inline(always)] + fn find_slice(&self, substr: T) -> Option<usize> { + self.input.find_slice(substr) + } +} + +/// Used to integrate `str`'s `parse()` method +pub trait ParseSlice<R> { + /// Succeeds if `parse()` succeededThe + /// + /// 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] { + #[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 { + #[inline(always)] + fn parse_slice(&self) -> Option<R> { + self.parse().ok() + } +} + +/// Convert a `Stream` into an appropriate `Output` type +pub trait UpdateSlice: Stream { + /// Convert an `Output` type to be used as `Stream` + fn update_slice(self, inner: Self::Slice) -> Self; +} + +impl<'a, T> UpdateSlice for &'a [T] +where + T: Clone + crate::lib::std::fmt::Debug, +{ + #[inline(always)] + fn update_slice(self, inner: Self::Slice) -> Self { + inner + } +} + +impl<'a> UpdateSlice for &'a str { + #[inline(always)] + fn update_slice(self, inner: Self::Slice) -> Self { + inner + } +} + +impl<'a> UpdateSlice for &'a Bytes { + #[inline(always)] + fn update_slice(self, inner: Self::Slice) -> Self { + Bytes::new(inner) + } +} + +impl<'a> UpdateSlice for &'a BStr { + #[inline(always)] + fn update_slice(self, inner: Self::Slice) -> Self { + BStr::new(inner) + } +} + +impl<I> UpdateSlice for Located<I> +where + I: UpdateSlice, +{ + #[inline(always)] + fn update_slice(mut self, inner: Self::Slice) -> Self { + self.input = I::update_slice(self.input, inner); + self + } +} + +impl<I, S> UpdateSlice for Stateful<I, S> +where + I: UpdateSlice, + S: Clone + crate::lib::std::fmt::Debug, +{ + #[inline(always)] + fn update_slice(mut self, inner: Self::Slice) -> Self { + self.input = I::update_slice(self.input, inner); + self + } +} + +impl<I> UpdateSlice for Partial<I> +where + I: UpdateSlice, +{ + #[inline(always)] + fn update_slice(self, inner: Self::Slice) -> Self { + Partial { + input: I::update_slice(self.input, inner), + partial: self.partial, + } + } +} + +/// Ensure checkpoint details are kept privazte +#[derive(Copy, Clone, Debug)] +pub struct Checkpoint<T>(T); + +/// A range bounded inclusively for counting parses performed +#[derive(PartialEq, Eq)] +pub struct Range { + pub(crate) start_inclusive: usize, + pub(crate) end_inclusive: Option<usize>, +} + +impl Range { + #[inline(always)] + fn raw(start_inclusive: usize, end_inclusive: Option<usize>) -> Self { + Self { + start_inclusive, + end_inclusive, + } + } +} + +impl crate::lib::std::ops::RangeBounds<usize> for Range { + #[inline(always)] + fn start_bound(&self) -> crate::lib::std::ops::Bound<&usize> { + crate::lib::std::ops::Bound::Included(&self.start_inclusive) + } + + #[inline(always)] + fn end_bound(&self) -> crate::lib::std::ops::Bound<&usize> { + if let Some(end_inclusive) = &self.end_inclusive { + crate::lib::std::ops::Bound::Included(end_inclusive) + } else { + crate::lib::std::ops::Bound::Unbounded + } + } +} + +impl From<usize> for Range { + #[inline(always)] + fn from(fixed: usize) -> Self { + (fixed..=fixed).into() + } +} + +impl From<crate::lib::std::ops::Range<usize>> for Range { + #[inline(always)] + fn from(range: crate::lib::std::ops::Range<usize>) -> Self { + let start_inclusive = range.start; + let end_inclusive = Some(range.end.saturating_sub(1)); + Self::raw(start_inclusive, end_inclusive) + } +} + +impl From<crate::lib::std::ops::RangeFull> for Range { + #[inline(always)] + fn from(_: crate::lib::std::ops::RangeFull) -> Self { + let start_inclusive = 0; + let end_inclusive = None; + Self::raw(start_inclusive, end_inclusive) + } +} + +impl From<crate::lib::std::ops::RangeFrom<usize>> for Range { + #[inline(always)] + fn from(range: crate::lib::std::ops::RangeFrom<usize>) -> Self { + let start_inclusive = range.start; + let end_inclusive = None; + Self::raw(start_inclusive, end_inclusive) + } +} + +impl From<crate::lib::std::ops::RangeTo<usize>> for Range { + #[inline(always)] + fn from(range: crate::lib::std::ops::RangeTo<usize>) -> Self { + let start_inclusive = 0; + let end_inclusive = Some(range.end.saturating_sub(1)); + Self::raw(start_inclusive, end_inclusive) + } +} + +impl From<crate::lib::std::ops::RangeInclusive<usize>> for Range { + #[inline(always)] + fn from(range: crate::lib::std::ops::RangeInclusive<usize>) -> Self { + let start_inclusive = *range.start(); + let end_inclusive = Some(*range.end()); + Self::raw(start_inclusive, end_inclusive) + } +} + +impl From<crate::lib::std::ops::RangeToInclusive<usize>> for Range { + #[inline(always)] + fn from(range: crate::lib::std::ops::RangeToInclusive<usize>) -> Self { + let start_inclusive = 0; + let end_inclusive = Some(range.end); + Self::raw(start_inclusive, end_inclusive) + } +} + +impl crate::lib::std::fmt::Display for Range { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + self.start_inclusive.fmt(f)?; + match self.end_inclusive { + Some(e) if e == self.start_inclusive => {} + Some(e) => { + "..=".fmt(f)?; + e.fmt(f)?; + } + None => { + "..".fmt(f)?; + } + } + Ok(()) + } +} + +impl crate::lib::std::fmt::Debug for Range { + fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { + write!(f, "{self}") + } +} + +/// Abstracts something which can extend an `Extend`. +/// Used to build modified input slices in `escaped_transform` +pub trait Accumulate<T>: Sized { + /// Create a new `Extend` of the correct type + fn initial(capacity: Option<usize>) -> Self; + /// Accumulate the input into an accumulator + fn accumulate(&mut self, acc: T); +} + +impl<T> Accumulate<T> for () { + #[inline(always)] + fn initial(_capacity: Option<usize>) -> Self {} + #[inline(always)] + fn accumulate(&mut self, _acc: T) {} +} + +impl<T> Accumulate<T> for usize { + #[inline(always)] + fn initial(_capacity: Option<usize>) -> Self { + 0 + } + #[inline(always)] + fn accumulate(&mut self, _acc: T) { + *self += 1; + } +} + +#[cfg(feature = "alloc")] +impl<T> Accumulate<T> for Vec<T> { + #[inline(always)] + fn initial(capacity: Option<usize>) -> Self { + match capacity { + Some(capacity) => Vec::with_capacity(clamp_capacity::<T>(capacity)), + None => Vec::new(), + } + } + #[inline(always)] + fn accumulate(&mut self, acc: T) { + self.push(acc); + } +} + +#[cfg(feature = "alloc")] +impl<'i, T: Clone> Accumulate<&'i [T]> for Vec<T> { + #[inline(always)] + fn initial(capacity: Option<usize>) -> Self { + match capacity { + Some(capacity) => Vec::with_capacity(clamp_capacity::<T>(capacity)), + None => Vec::new(), + } + } + #[inline(always)] + fn accumulate(&mut self, acc: &'i [T]) { + self.extend(acc.iter().cloned()); + } +} + +#[cfg(feature = "alloc")] +impl Accumulate<char> for String { + #[inline(always)] + fn initial(capacity: Option<usize>) -> Self { + match capacity { + Some(capacity) => String::with_capacity(clamp_capacity::<char>(capacity)), + None => String::new(), + } + } + #[inline(always)] + fn accumulate(&mut self, acc: char) { + self.push(acc); + } +} + +#[cfg(feature = "alloc")] +impl<'i> Accumulate<&'i str> for String { + #[inline(always)] + fn initial(capacity: Option<usize>) -> Self { + match capacity { + Some(capacity) => String::with_capacity(clamp_capacity::<char>(capacity)), + None => String::new(), + } + } + #[inline(always)] + fn accumulate(&mut self, acc: &'i str) { + self.push_str(acc); + } +} + +#[cfg(feature = "alloc")] +impl<K, V> Accumulate<(K, V)> for BTreeMap<K, V> +where + K: crate::lib::std::cmp::Ord, +{ + #[inline(always)] + fn initial(_capacity: Option<usize>) -> Self { + BTreeMap::new() + } + #[inline(always)] + fn accumulate(&mut self, (key, value): (K, V)) { + self.insert(key, value); + } +} + +#[cfg(feature = "std")] +impl<K, V> Accumulate<(K, V)> for HashMap<K, V> +where + K: crate::lib::std::cmp::Eq + crate::lib::std::hash::Hash, +{ + #[inline(always)] + fn initial(capacity: Option<usize>) -> Self { + match capacity { + Some(capacity) => HashMap::with_capacity(clamp_capacity::<(K, V)>(capacity)), + None => HashMap::new(), + } + } + #[inline(always)] + fn accumulate(&mut self, (key, value): (K, V)) { + self.insert(key, value); + } +} + +#[cfg(feature = "alloc")] +#[inline] +pub(crate) fn clamp_capacity<T>(capacity: usize) -> usize { + /// Don't pre-allocate more than 64KiB when calling `Vec::with_capacity`. + /// + /// Pre-allocating memory is a nice optimization but count fields can't + /// always be trusted. We should clamp initial capacities to some reasonable + /// amount. This reduces the risk of a bogus count value triggering a panic + /// due to an OOM error. + /// + /// This does not affect correctness. `winnow` will always read the full number + /// of elements regardless of the capacity cap. + const MAX_INITIAL_CAPACITY_BYTES: usize = 65536; + + let max_initial_capacity = + MAX_INITIAL_CAPACITY_BYTES / crate::lib::std::mem::size_of::<T>().max(1); + capacity.min(max_initial_capacity) +} + +/// Helper trait to convert numbers to usize. +/// +/// By default, usize implements `From<u8>` and `From<u16>` but not +/// `From<u32>` and `From<u64>` because that would be invalid on some +/// platforms. This trait implements the conversion for platforms +/// with 32 and 64 bits pointer platforms +pub trait ToUsize { + /// converts self to usize + fn to_usize(&self) -> usize; +} + +impl ToUsize for u8 { + #[inline(always)] + fn to_usize(&self) -> usize { + *self as usize + } +} + +impl ToUsize for u16 { + #[inline(always)] + fn to_usize(&self) -> usize { + *self as usize + } +} + +impl ToUsize for usize { + #[inline(always)] + fn to_usize(&self) -> usize { + *self + } +} + +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +impl ToUsize for u32 { + #[inline(always)] + fn to_usize(&self) -> usize { + *self as usize + } +} + +#[cfg(target_pointer_width = "64")] +impl ToUsize for u64 { + #[inline(always)] + fn to_usize(&self) -> usize { + *self as usize + } +} + +/// Transforms a token into a char for basic string parsing +#[allow(clippy::len_without_is_empty)] +#[allow(clippy::wrong_self_convention)] +pub trait AsChar { + /// Makes a char from self + /// + /// # Example + /// + /// ``` + /// use winnow::stream::AsChar as _; + /// + /// assert_eq!('a'.as_char(), 'a'); + /// assert_eq!(u8::MAX.as_char(), std::char::from_u32(u8::MAX as u32).unwrap()); + /// ``` + fn as_char(self) -> char; + + /// Tests that self is an alphabetic character + /// + /// **Warning:** for `&str` it recognizes alphabetic + /// characters outside of the 52 ASCII letters + fn is_alpha(self) -> bool; + + /// Tests that self is an alphabetic character + /// or a decimal digit + fn is_alphanum(self) -> bool; + /// Tests that self is a decimal digit + fn is_dec_digit(self) -> bool; + /// Tests that self is an hex digit + fn is_hex_digit(self) -> bool; + /// Tests that self is an octal digit + fn is_oct_digit(self) -> bool; + /// Gets the len in bytes for self + fn len(self) -> usize; + /// Tests that self is ASCII space or tab + fn is_space(self) -> bool; + /// Tests if byte is ASCII newline: \n + fn is_newline(self) -> bool; +} + +impl AsChar for u8 { + #[inline(always)] + fn as_char(self) -> char { + self as char + } + #[inline] + fn is_alpha(self) -> bool { + matches!(self, 0x41..=0x5A | 0x61..=0x7A) + } + #[inline] + fn is_alphanum(self) -> bool { + self.is_alpha() || self.is_dec_digit() + } + #[inline] + fn is_dec_digit(self) -> bool { + matches!(self, 0x30..=0x39) + } + #[inline] + fn is_hex_digit(self) -> bool { + matches!(self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66) + } + #[inline] + fn is_oct_digit(self) -> bool { + matches!(self, 0x30..=0x37) + } + #[inline] + fn len(self) -> usize { + 1 + } + #[inline] + fn is_space(self) -> bool { + self == b' ' || self == b'\t' + } + #[inline] + fn is_newline(self) -> bool { + self == b'\n' + } +} +impl<'a> AsChar for &'a u8 { + #[inline(always)] + fn as_char(self) -> char { + *self as char + } + #[inline] + fn is_alpha(self) -> bool { + matches!(*self, 0x41..=0x5A | 0x61..=0x7A) + } + #[inline] + fn is_alphanum(self) -> bool { + self.is_alpha() || self.is_dec_digit() + } + #[inline] + fn is_dec_digit(self) -> bool { + matches!(*self, 0x30..=0x39) + } + #[inline] + fn is_hex_digit(self) -> bool { + matches!(*self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66) + } + #[inline] + fn is_oct_digit(self) -> bool { + matches!(*self, 0x30..=0x37) + } + #[inline] + fn len(self) -> usize { + 1 + } + #[inline] + fn is_space(self) -> bool { + *self == b' ' || *self == b'\t' + } + #[inline] + fn is_newline(self) -> bool { + *self == b'\n' + } +} + +impl AsChar for char { + #[inline(always)] + fn as_char(self) -> char { + self + } + #[inline] + fn is_alpha(self) -> bool { + self.is_ascii_alphabetic() + } + #[inline] + fn is_alphanum(self) -> bool { + self.is_alpha() || self.is_dec_digit() + } + #[inline] + fn is_dec_digit(self) -> bool { + self.is_ascii_digit() + } + #[inline] + fn is_hex_digit(self) -> bool { + self.is_ascii_hexdigit() + } + #[inline] + fn is_oct_digit(self) -> bool { + self.is_digit(8) + } + #[inline] + fn len(self) -> usize { + self.len_utf8() + } + #[inline] + fn is_space(self) -> bool { + self == ' ' || self == '\t' + } + #[inline] + fn is_newline(self) -> bool { + self == '\n' + } +} + +impl<'a> AsChar for &'a char { + #[inline(always)] + fn as_char(self) -> char { + *self + } + #[inline] + fn is_alpha(self) -> bool { + self.is_ascii_alphabetic() + } + #[inline] + fn is_alphanum(self) -> bool { + self.is_alpha() || self.is_dec_digit() + } + #[inline] + fn is_dec_digit(self) -> bool { + self.is_ascii_digit() + } + #[inline] + fn is_hex_digit(self) -> bool { + self.is_ascii_hexdigit() + } + #[inline] + fn is_oct_digit(self) -> bool { + self.is_digit(8) + } + #[inline] + fn len(self) -> usize { + self.len_utf8() + } + #[inline] + fn is_space(self) -> bool { + *self == ' ' || *self == '\t' + } + #[inline] + fn is_newline(self) -> bool { + *self == '\n' + } +} + +/// Check if a token in 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 +/// - `b'c'` and `'c'` +/// - `b""` and `""` +/// - `|c| true` +/// - `b'a'..=b'z'`, `'a'..='z'` (etc for each [range type][std::ops]) +/// - `(pattern1, pattern2, ...)` +/// +/// # Example +/// +/// For example, you could implement `hex_digit0` as: +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::token::take_while; +/// fn hex_digit1<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { +/// take_while(1.., ('a'..='f', 'A'..='F', '0'..='9')).parse_next(input) +/// } +/// +/// assert_eq!(hex_digit1.parse_peek("21cZ"), Ok(("Z", "21c"))); +/// assert_eq!(hex_digit1.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); +/// assert_eq!(hex_digit1.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +pub trait ContainsToken<T> { + /// Returns true if self contains the token + fn contains_token(&self, token: T) -> bool; +} + +impl ContainsToken<u8> for u8 { + #[inline(always)] + fn contains_token(&self, token: u8) -> bool { + *self == token + } +} + +impl<'a> ContainsToken<&'a u8> for u8 { + #[inline(always)] + fn contains_token(&self, token: &u8) -> bool { + self.contains_token(*token) + } +} + +impl ContainsToken<char> for u8 { + #[inline(always)] + fn contains_token(&self, token: char) -> bool { + self.as_char() == token + } +} + +impl<'a> ContainsToken<&'a char> for u8 { + #[inline(always)] + fn contains_token(&self, token: &char) -> bool { + self.contains_token(*token) + } +} + +impl<C: AsChar> ContainsToken<C> for char { + #[inline(always)] + fn contains_token(&self, token: C) -> bool { + *self == token.as_char() + } +} + +impl<C, F: Fn(C) -> bool> ContainsToken<C> for F { + #[inline(always)] + fn contains_token(&self, token: C) -> bool { + self(token) + } +} + +impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> for crate::lib::std::ops::Range<C2> { + #[inline(always)] + fn contains_token(&self, token: C1) -> bool { + let start = self.start.clone().as_char(); + let end = self.end.clone().as_char(); + (start..end).contains(&token.as_char()) + } +} + +impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> + for crate::lib::std::ops::RangeInclusive<C2> +{ + #[inline(always)] + fn contains_token(&self, token: C1) -> bool { + let start = self.start().clone().as_char(); + let end = self.end().clone().as_char(); + (start..=end).contains(&token.as_char()) + } +} + +impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> for crate::lib::std::ops::RangeFrom<C2> { + #[inline(always)] + fn contains_token(&self, token: C1) -> bool { + let start = self.start.clone().as_char(); + (start..).contains(&token.as_char()) + } +} + +impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> for crate::lib::std::ops::RangeTo<C2> { + #[inline(always)] + fn contains_token(&self, token: C1) -> bool { + let end = self.end.clone().as_char(); + (..end).contains(&token.as_char()) + } +} + +impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> + for crate::lib::std::ops::RangeToInclusive<C2> +{ + #[inline(always)] + fn contains_token(&self, token: C1) -> bool { + let end = self.end.clone().as_char(); + (..=end).contains(&token.as_char()) + } +} + +impl<C1: AsChar> ContainsToken<C1> for crate::lib::std::ops::RangeFull { + #[inline(always)] + fn contains_token(&self, _token: C1) -> bool { + true + } +} + +impl<C: AsChar> ContainsToken<C> for &'_ [u8] { + #[inline] + fn contains_token(&self, token: C) -> bool { + let token = token.as_char(); + self.iter().any(|t| t.as_char() == token) + } +} + +impl<C: AsChar> ContainsToken<C> for &'_ [char] { + #[inline] + fn contains_token(&self, token: C) -> bool { + let token = token.as_char(); + self.iter().any(|t| *t == token) + } +} + +impl<const LEN: usize, C: AsChar> ContainsToken<C> for &'_ [u8; LEN] { + #[inline] + fn contains_token(&self, token: C) -> bool { + let token = token.as_char(); + self.iter().any(|t| t.as_char() == token) + } +} + +impl<const LEN: usize, C: AsChar> ContainsToken<C> for &'_ [char; LEN] { + #[inline] + fn contains_token(&self, token: C) -> bool { + let token = token.as_char(); + self.iter().any(|t| *t == token) + } +} + +impl<const LEN: usize, C: AsChar> ContainsToken<C> for [u8; LEN] { + #[inline] + fn contains_token(&self, token: C) -> bool { + let token = token.as_char(); + self.iter().any(|t| t.as_char() == token) + } +} + +impl<const LEN: usize, C: AsChar> ContainsToken<C> for [char; LEN] { + #[inline] + fn contains_token(&self, token: C) -> bool { + let token = token.as_char(); + self.iter().any(|t| *t == token) + } +} + +impl<T> ContainsToken<T> for () { + #[inline(always)] + fn contains_token(&self, _token: T) -> bool { + false + } +} + +macro_rules! impl_contains_token_for_tuple { + ($($haystack:ident),+) => ( + #[allow(non_snake_case)] + impl<T, $($haystack),+> ContainsToken<T> for ($($haystack),+,) + where + T: Clone, + $($haystack: ContainsToken<T>),+ + { + #[inline] + fn contains_token(&self, token: T) -> bool { + let ($(ref $haystack),+,) = *self; + $($haystack.contains_token(token.clone()) || )+ false + } + } + ) +} + +macro_rules! impl_contains_token_for_tuples { + ($haystack1:ident, $($haystack:ident),+) => { + impl_contains_token_for_tuples!(__impl $haystack1; $($haystack),+); + }; + (__impl $($haystack:ident),+; $haystack1:ident $(,$haystack2:ident)*) => { + impl_contains_token_for_tuple!($($haystack),+); + impl_contains_token_for_tuples!(__impl $($haystack),+, $haystack1; $($haystack2),*); + }; + (__impl $($haystack:ident),+;) => { + impl_contains_token_for_tuple!($($haystack),+); + } +} + +impl_contains_token_for_tuples!( + F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21 +); + +#[cfg(feature = "simd")] +#[inline(always)] +fn memchr(token: u8, slice: &[u8]) -> Option<usize> { + memchr::memchr(token, slice) +} + +#[cfg(not(feature = "simd"))] +#[inline(always)] +fn memchr(token: u8, slice: &[u8]) -> Option<usize> { + slice.iter().position(|t| *t == token) +} + +#[cfg(feature = "simd")] +#[inline(always)] +fn memmem(slice: &[u8], tag: &[u8]) -> Option<usize> { + if tag.len() > slice.len() { + return None; + } + + let (&substr_first, substr_rest) = match tag.split_first() { + Some(split) => split, + // an empty substring is found at position 0 + // This matches the behavior of str.find(""). + None => return Some(0), + }; + + if substr_rest.is_empty() { + return memchr::memchr(substr_first, slice); + } + + let mut offset = 0; + let haystack = &slice[..slice.len() - substr_rest.len()]; + + while let Some(position) = memchr::memchr(substr_first, &haystack[offset..]) { + offset += position; + let next_offset = offset + 1; + if &slice[next_offset..][..substr_rest.len()] == substr_rest { + return Some(offset); + } + + offset = next_offset; + } + + None +} + +#[cfg(not(feature = "simd"))] +fn memmem(slice: &[u8], tag: &[u8]) -> Option<usize> { + for i in 0..slice.len() { + let subslice = &slice[i..]; + if subslice.starts_with(tag) { + return Some(i); + } + } + None +} diff --git a/vendor/winnow/src/stream/tests.rs b/vendor/winnow/src/stream/tests.rs new file mode 100644 index 0000000..2dbb807 --- /dev/null +++ b/vendor/winnow/src/stream/tests.rs @@ -0,0 +1,131 @@ +#[cfg(feature = "std")] +use proptest::prelude::*; + +use super::*; + +#[test] +fn test_offset_u8() { + let s = b"abcd123"; + let a = &s[..]; + let b = &a[2..]; + let c = &a[..4]; + let d = &a[3..5]; + assert_eq!(b.offset_from(&a), 2); + assert_eq!(c.offset_from(&a), 0); + assert_eq!(d.offset_from(&a), 3); +} + +#[test] +fn test_offset_str() { + let a = "abcřèÂßÇd123"; + let b = &a[7..]; + let c = &a[..5]; + let d = &a[5..9]; + assert_eq!(b.offset_from(&a), 7); + assert_eq!(c.offset_from(&a), 0); + assert_eq!(d.offset_from(&a), 5); +} + +#[test] +#[cfg(feature = "alloc")] +fn test_bit_stream_empty() { + let i = (&b""[..], 0); + + let actual = i.iter_offsets().collect::<crate::lib::std::vec::Vec<_>>(); + assert_eq!(actual, vec![]); + + let actual = i.eof_offset(); + assert_eq!(actual, 0); + + let actual = i.peek_token(); + assert_eq!(actual, None); + + let actual = i.offset_for(|b| b); + assert_eq!(actual, None); + + let actual = i.offset_at(1); + assert_eq!(actual, Err(Needed::new(1))); + + let (actual_input, actual_slice) = i.peek_slice(0); + assert_eq!(actual_input, (&b""[..], 0)); + assert_eq!(actual_slice, (&b""[..], 0, 0)); +} + +#[test] +#[cfg(feature = "alloc")] +fn test_bit_offset_empty() { + let i = (&b""[..], 0); + + let actual = i.offset_from(&i); + assert_eq!(actual, 0); +} + +#[cfg(feature = "std")] +proptest! { + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn bit_stream(byte_len in 0..20usize, start in 0..160usize) { + bit_stream_inner(byte_len, start); + } +} + +#[cfg(feature = "std")] +fn bit_stream_inner(byte_len: usize, start: usize) { + let start = start.min(byte_len * 8); + let start_byte = start / 8; + let start_bit = start % 8; + + let bytes = vec![0b1010_1010; byte_len]; + let i = (&bytes[start_byte..], start_bit); + + let mut curr_i = i; + let mut curr_offset = 0; + while let Some((next_i, _token)) = curr_i.peek_token() { + let to_offset = curr_i.offset_from(&i); + assert_eq!(curr_offset, to_offset); + + let (slice_i, _) = i.peek_slice(curr_offset); + assert_eq!(curr_i, slice_i); + + let at_offset = i.offset_at(curr_offset).unwrap(); + assert_eq!(curr_offset, at_offset); + + let eof_offset = curr_i.eof_offset(); + let (next_eof_i, eof_slice) = curr_i.peek_slice(eof_offset); + assert_eq!(next_eof_i, (&b""[..], 0)); + let eof_slice_i = (eof_slice.0, eof_slice.1); + assert_eq!(eof_slice_i, curr_i); + + curr_offset += 1; + curr_i = next_i; + } + assert_eq!(i.eof_offset(), curr_offset); +} + +#[test] +fn test_partial_complete() { + let mut i = Partial::new(&b""[..]); + assert!(Partial::<&[u8]>::is_partial_supported()); + + assert!(i.is_partial(), "incomplete by default"); + let incomplete_state = i.complete(); + assert!(!i.is_partial(), "the stream should be marked as complete"); + + i.restore_partial(incomplete_state); + assert!(i.is_partial(), "incomplete stream state should be restored"); +} + +#[test] +fn test_custom_slice() { + type Token = usize; + type TokenSlice<'i> = &'i [Token]; + + let mut tokens: TokenSlice<'_> = &[1, 2, 3, 4]; + + let input = &mut tokens; + let start = input.checkpoint(); + let _ = input.next_token(); + let _ = input.next_token(); + let offset = input.offset_from(&start); + assert_eq!(offset, 2); +} diff --git a/vendor/winnow/src/token/mod.rs b/vendor/winnow/src/token/mod.rs new file mode 100644 index 0000000..ec31cb4 --- /dev/null +++ b/vendor/winnow/src/token/mod.rs @@ -0,0 +1,1062 @@ +//! Parsers extracting tokens from the stream + +#[cfg(test)] +mod tests; + +use crate::error::ErrMode; +use crate::error::ErrorKind; +use crate::error::Needed; +use crate::error::ParserError; +use crate::lib::std::result::Result::Ok; +use crate::stream::Range; +use crate::stream::{Compare, CompareResult, ContainsToken, FindSlice, SliceLen, Stream}; +use crate::stream::{StreamIsPartial, ToUsize}; +use crate::trace::trace; +use crate::PResult; +use crate::Parser; + +/// Matches one token +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{token::any, error::ErrMode, error::{InputError, ErrorKind}}; +/// # use winnow::prelude::*; +/// fn parser(input: &str) -> IResult<&str, char> { +/// any.parse_peek(input) +/// } +/// +/// assert_eq!(parser("abc"), Ok(("bc",'a'))); +/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); +/// ``` +/// +/// ```rust +/// # use winnow::{token::any, error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// assert_eq!(any::<_, InputError<_>>.parse_peek(Partial::new("abc")), Ok((Partial::new("bc"),'a'))); +/// assert_eq!(any::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +#[inline(always)] +#[doc(alias = "token")] +pub fn any<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Token, E> +where + I: StreamIsPartial, + I: Stream, +{ + trace("any", move |input: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + any_::<_, _, true>(input) + } else { + any_::<_, _, true>(input) + } + }) + .parse_next(input) +} + +fn any_<I, E: ParserError<I>, const PARTIAL: bool>( + input: &mut I, +) -> PResult<<I as Stream>::Token, E> +where + I: StreamIsPartial, + I: Stream, +{ + input.next_token().ok_or_else(|| { + if PARTIAL && input.is_partial() { + ErrMode::Incomplete(Needed::new(1)) + } else { + ErrMode::from_error_kind(input, ErrorKind::Token) + } + }) +} + +/// Recognizes a literal +/// +/// The input data will be compared to the tag 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 +/// +/// **Note:** [`Parser`] is implemented for strings and byte strings as a convenience (complete +/// only) +/// +/// # 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) +/// } +/// +/// 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::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) +/// } +/// +/// 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("S")), Err(ErrMode::Backtrack(InputError::new(Partial::new("S"), ErrorKind::Tag)))); +/// assert_eq!(parser(Partial::new("H")), Err(ErrMode::Incomplete(Needed::new(4)))); +/// ``` +#[inline(always)] +#[doc(alias = "literal")] +#[doc(alias = "bytes")] +#[doc(alias = "just")] +pub fn tag<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", move |i: &mut I| { + let t = tag.clone(); + if <I as StreamIsPartial>::is_partial_supported() { + tag_::<_, _, _, true>(i, t) + } else { + tag_::<_, _, _, false>(i, t) + } + }) +} + +fn tag_<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(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)) + } + } +} + +/// Recognizes a case insensitive literal. +/// +/// 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")] +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) + } + }) +} + +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] +/// +/// **Note:** [`Parser`] is implemented as a convenience (complete +/// only) for +/// - `u8` +/// - `char` +/// +/// *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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::token::one_of; +/// assert_eq!(one_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek("b"), Ok(("", 'b'))); +/// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Verify)))); +/// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); +/// +/// fn parser_fn(i: &str) -> IResult<&str, char> { +/// one_of(|c| c == 'a' || c == 'b').parse_peek(i) +/// } +/// assert_eq!(parser_fn("abc"), Ok(("bc", 'a'))); +/// assert_eq!(parser_fn("cd"), Err(ErrMode::Backtrack(InputError::new("cd", ErrorKind::Verify)))); +/// assert_eq!(parser_fn(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); +/// ``` +/// +/// ``` +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// # use winnow::token::one_of; +/// assert_eq!(one_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek(Partial::new("b")), Ok((Partial::new(""), 'b'))); +/// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("bc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("bc"), ErrorKind::Verify)))); +/// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// +/// fn parser_fn(i: Partial<&str>) -> IResult<Partial<&str>, char> { +/// one_of(|c| c == 'a' || c == 'b').parse_peek(i) +/// } +/// assert_eq!(parser_fn(Partial::new("abc")), Ok((Partial::new("bc"), 'a'))); +/// assert_eq!(parser_fn(Partial::new("cd")), Err(ErrMode::Backtrack(InputError::new(Partial::new("cd"), ErrorKind::Verify)))); +/// assert_eq!(parser_fn(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +#[inline(always)] +#[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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: Clone, + T: ContainsToken<<I as Stream>::Token>, +{ + trace( + "one_of", + any.verify(move |t: &<I as Stream>::Token| list.contains_token(t.clone())), + ) +} + +/// Recognize a token that does not match the [pattern][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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; +/// # use winnow::prelude::*; +/// # use winnow::token::none_of; +/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek("z"), Ok(("", 'z'))); +/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b']).parse_peek("a"), Err(ErrMode::Backtrack(InputError::new("a", ErrorKind::Verify)))); +/// assert_eq!(none_of::<_, _, InputError<_>>('a').parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); +/// ``` +/// +/// ``` +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// # use winnow::token::none_of; +/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek(Partial::new("z")), Ok((Partial::new(""), 'z'))); +/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b']).parse_peek(Partial::new("a")), Err(ErrMode::Backtrack(InputError::new(Partial::new("a"), ErrorKind::Verify)))); +/// 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> +where + I: StreamIsPartial, + I: Stream, + <I as Stream>::Token: Clone, + T: ContainsToken<<I as Stream>::Token>, +{ + trace( + "none_of", + any.verify(move |t: &<I as Stream>::Token| !list.contains_token(t.clone())), + ) +} + +/// Recognize the longest (m <= len <= n) input slice that matches the [pattern][ContainsToken] +/// +/// It will return an `ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice))` if the pattern 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. +/// +/// To recognize a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::recognize`]. +/// +/// # Example +/// +/// Zero or more tokens: +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::token::take_while; +/// use winnow::stream::AsChar; +/// +/// fn alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { +/// take_while(0.., AsChar::is_alpha).parse_peek(s) +/// } +/// +/// assert_eq!(alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..]))); +/// assert_eq!(alpha(b"12345"), Ok((&b"12345"[..], &b""[..]))); +/// assert_eq!(alpha(b"latin"), Ok((&b""[..], &b"latin"[..]))); +/// assert_eq!(alpha(b""), Ok((&b""[..], &b""[..]))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::token::take_while; +/// use winnow::stream::AsChar; +/// +/// fn alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { +/// take_while(0.., AsChar::is_alpha).parse_peek(s) +/// } +/// +/// assert_eq!(alpha(Partial::new(b"latin123")), Ok((Partial::new(&b"123"[..]), &b"latin"[..]))); +/// assert_eq!(alpha(Partial::new(b"12345")), Ok((Partial::new(&b"12345"[..]), &b""[..]))); +/// assert_eq!(alpha(Partial::new(b"latin")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(alpha(Partial::new(b"")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +/// +/// One or more tokens: +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::token::take_while; +/// use winnow::stream::AsChar; +/// +/// fn alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { +/// take_while(1.., AsChar::is_alpha).parse_peek(s) +/// } +/// +/// assert_eq!(alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..]))); +/// assert_eq!(alpha(b"latin"), Ok((&b""[..], &b"latin"[..]))); +/// assert_eq!(alpha(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice)))); +/// +/// fn hex(s: &str) -> IResult<&str, &str> { +/// take_while(1.., ('0'..='9', 'A'..='F')).parse_peek(s) +/// } +/// +/// assert_eq!(hex("123 and voila"), Ok((" and voila", "123"))); +/// assert_eq!(hex("DEADBEEF and others"), Ok((" and others", "DEADBEEF"))); +/// assert_eq!(hex("BADBABEsomething"), Ok(("something", "BADBABE"))); +/// assert_eq!(hex("D15EA5E"), Ok(("", "D15EA5E"))); +/// assert_eq!(hex(""), 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_while; +/// use winnow::stream::AsChar; +/// +/// fn alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { +/// take_while(1.., AsChar::is_alpha).parse_peek(s) +/// } +/// +/// assert_eq!(alpha(Partial::new(b"latin123")), Ok((Partial::new(&b"123"[..]), &b"latin"[..]))); +/// assert_eq!(alpha(Partial::new(b"latin")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(alpha(Partial::new(b"12345")), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"12345"[..]), ErrorKind::Slice)))); +/// +/// fn hex(s: Partial<&str>) -> IResult<Partial<&str>, &str> { +/// take_while(1.., ('0'..='9', 'A'..='F')).parse_peek(s) +/// } +/// +/// assert_eq!(hex(Partial::new("123 and voila")), Ok((Partial::new(" and voila"), "123"))); +/// assert_eq!(hex(Partial::new("DEADBEEF and others")), Ok((Partial::new(" and others"), "DEADBEEF"))); +/// assert_eq!(hex(Partial::new("BADBABEsomething")), Ok((Partial::new("something"), "BADBABE"))); +/// assert_eq!(hex(Partial::new("D15EA5E")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(hex(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// ``` +/// +/// Arbitrary amount of tokens: +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::token::take_while; +/// use winnow::stream::AsChar; +/// +/// fn short_alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { +/// take_while(3..=6, AsChar::is_alpha).parse_peek(s) +/// } +/// +/// assert_eq!(short_alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..]))); +/// assert_eq!(short_alpha(b"lengthy"), Ok((&b"y"[..], &b"length"[..]))); +/// assert_eq!(short_alpha(b"latin"), Ok((&b""[..], &b"latin"[..]))); +/// assert_eq!(short_alpha(b"ed"), Err(ErrMode::Backtrack(InputError::new(&b"ed"[..], ErrorKind::Slice)))); +/// assert_eq!(short_alpha(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::token::take_while; +/// use winnow::stream::AsChar; +/// +/// fn short_alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { +/// take_while(3..=6, AsChar::is_alpha).parse_peek(s) +/// } +/// +/// assert_eq!(short_alpha(Partial::new(b"latin123")), Ok((Partial::new(&b"123"[..]), &b"latin"[..]))); +/// assert_eq!(short_alpha(Partial::new(b"lengthy")), Ok((Partial::new(&b"y"[..]), &b"length"[..]))); +/// assert_eq!(short_alpha(Partial::new(b"latin")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(short_alpha(Partial::new(b"ed")), Err(ErrMode::Incomplete(Needed::new(1)))); +/// assert_eq!(short_alpha(Partial::new(b"12345")), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"12345"[..]), ErrorKind::Slice)))); +/// ``` +#[inline(always)] +#[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> +where + I: StreamIsPartial, + I: Stream, + T: ContainsToken<<I as Stream>::Token>, +{ + let Range { + start_inclusive, + end_inclusive, + } = range.into(); + trace("take_while", move |i: &mut I| { + match (start_inclusive, end_inclusive) { + (0, None) => { + if <I as StreamIsPartial>::is_partial_supported() { + take_while0_::<_, _, _, true>(i, &list) + } else { + take_while0_::<_, _, _, false>(i, &list) + } + } + (1, None) => { + if <I as StreamIsPartial>::is_partial_supported() { + take_while1_::<_, _, _, true>(i, &list) + } else { + take_while1_::<_, _, _, false>(i, &list) + } + } + (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) + } else { + take_while_m_n_::<_, _, _, false>(i, start, end, &list) + } + } + } + }) +} + +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>, +{ + let e: ErrorKind = ErrorKind::Slice; + if PARTIAL && input.is_partial() { + take_till1_partial(input, |c| !list.contains_token(c), e) + } else { + take_till1_complete(input, |c| !list.contains_token(c), e) + } +} + +/// 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>>( + 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)))?; + 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>>( + input: &mut I, + predicate: P, + e: ErrorKind, +) -> 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)))?; + 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, + e: ErrorKind, +) -> PResult<<I as Stream>::Slice, E> +where + P: Fn(I::Token) -> bool, +{ + let offset = input + .offset_for(predicate) + .unwrap_or_else(|| input.eof_offset()); + if offset == 0 { + Err(ErrMode::from_error_kind(input, e)) + } else { + Ok(input.next_slice(offset)) + } +} + +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>, +{ + if n < m { + return Err(ErrMode::assert(input, "`m` should be <= `n`")); + } + + let mut final_count = 0; + for (processed, (offset, token)) in input.iter_offsets().enumerate() { + if !list.contains_token(token) { + if processed < m { + return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); + } else { + return Ok(input.next_slice(offset)); + } + } else { + if processed == n { + return Ok(input.next_slice(offset)); + } + final_count = processed + 1; + } + } + if PARTIAL && input.is_partial() { + if final_count == n { + Ok(input.finish()) + } else { + let needed = if m > input.eof_offset() { + m - input.eof_offset() + } else { + 1 + }; + Err(ErrMode::Incomplete(Needed::new(needed))) + } + } else { + if m <= final_count { + Ok(input.finish()) + } else { + Err(ErrMode::from_error_kind(input, ErrorKind::Slice)) + } + } +} + +/// 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)))); +/// ``` +#[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)] +#[doc(alias = "is_not")] +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| { + let e: ErrorKind = ErrorKind::Slice; + if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() { + take_till1_partial(i, |c| list.contains_token(c), e) + } else { + take_till1_complete(i, |c| list.contains_token(c), e) + } + }) +} + +/// 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 +/// 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)` +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::token::take; +/// +/// fn take6(s: &str) -> IResult<&str, &str> { +/// take(6usize).parse_peek(s) +/// } +/// +/// assert_eq!(take6("1234567"), Ok(("7", "123456"))); +/// assert_eq!(take6("things"), Ok(("", "things"))); +/// assert_eq!(take6("short"), Err(ErrMode::Backtrack(InputError::new("short", ErrorKind::Slice)))); +/// assert_eq!(take6(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// ``` +/// +/// The units that are taken will depend on the input type. For example, for a +/// `&str` it will take a number of `char`'s, whereas for a `&[u8]` it will +/// take that many `u8`'s: +/// +/// ```rust +/// # use winnow::prelude::*; +/// use winnow::error::InputError; +/// use winnow::token::take; +/// +/// assert_eq!(take::<_, _, InputError<_>>(1usize).parse_peek("💙"), Ok(("", "💙"))); +/// assert_eq!(take::<_, _, InputError<_>>(1usize).parse_peek("💙".as_bytes()), Ok((b"\x9F\x92\x99".as_ref(), b"\xF0".as_ref()))); +/// ``` +/// +/// ```rust +/// # use winnow::prelude::*; +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::Partial; +/// use winnow::token::take; +/// +/// fn take6(s: Partial<&str>) -> IResult<Partial<&str>, &str> { +/// take(6usize).parse_peek(s) +/// } +/// +/// assert_eq!(take6(Partial::new("1234567")), Ok((Partial::new("7"), "123456"))); +/// assert_eq!(take6(Partial::new("things")), Ok((Partial::new(""), "things"))); +/// // `Unknown` as we don't know the number of bytes that `count` corresponds to +/// 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> +where + I: StreamIsPartial, + I: Stream, + C: ToUsize, +{ + let c = count.to_usize(); + trace("take", move |i: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + take_::<_, _, true>(i, c) + } else { + take_::<_, _, false>(i, c) + } + }) +} + +fn take_<I, Error: ParserError<I>, const PARTIAL: bool>( + i: &mut I, + c: usize, +) -> PResult<<I as Stream>::Slice, Error> +where + I: StreamIsPartial, + I: Stream, +{ + match i.offset_at(c) { + Ok(offset) => Ok(i.next_slice(offset)), + Err(e) if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(e)), + Err(_needed) => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), + } +} + +/// Recognize the input slice up to the first occurrence of the literal. +/// +/// It doesn't consume the pattern. +/// +/// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` +/// if the pattern 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::token::take_until0; +/// +/// fn until_eof(s: &str) -> IResult<&str, &str> { +/// take_until0("eof").parse_peek(s) +/// } +/// +/// assert_eq!(until_eof("hello, worldeof"), Ok(("eof", "hello, world"))); +/// assert_eq!(until_eof("hello, world"), Err(ErrMode::Backtrack(InputError::new("hello, world", ErrorKind::Slice)))); +/// assert_eq!(until_eof(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// assert_eq!(until_eof("1eof2eof"), Ok(("eof2eof", "1"))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::token::take_until0; +/// +/// fn until_eof(s: Partial<&str>) -> IResult<Partial<&str>, &str> { +/// take_until0("eof").parse_peek(s) +/// } +/// +/// assert_eq!(until_eof(Partial::new("hello, worldeof")), Ok((Partial::new("eof"), "hello, world"))); +/// assert_eq!(until_eof(Partial::new("hello, world")), Err(ErrMode::Incomplete(Needed::Unknown))); +/// assert_eq!(until_eof(Partial::new("hello, worldeo")), Err(ErrMode::Incomplete(Needed::Unknown))); +/// assert_eq!(until_eof(Partial::new("1eof2eof")), Ok((Partial::new("eof2eof"), "1"))); +/// ``` +#[inline(always)] +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, +{ + trace("take_until0", move |i: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + take_until0_::<_, _, _, true>(i, tag.clone()) + } else { + take_until0_::<_, _, _, false>(i, tag.clone()) + } + }) +} + +fn take_until0_<T, I, Error: ParserError<I>, const PARTIAL: bool>( + i: &mut I, + t: T, +) -> PResult<<I as Stream>::Slice, Error> +where + I: StreamIsPartial, + I: Stream + FindSlice<T>, + T: SliceLen, +{ + match i.find_slice(t) { + Some(offset) => Ok(i.next_slice(offset)), + None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), + None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), + } +} + +/// Recognize the non empty input slice up to the first occurrence of the literal. +/// +/// It doesn't consume the pattern. +/// +/// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` +/// if the pattern 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. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// use winnow::token::take_until1; +/// +/// fn until_eof(s: &str) -> IResult<&str, &str> { +/// take_until1("eof").parse_peek(s) +/// } +/// +/// assert_eq!(until_eof("hello, worldeof"), Ok(("eof", "hello, world"))); +/// assert_eq!(until_eof("hello, world"), Err(ErrMode::Backtrack(InputError::new("hello, world", ErrorKind::Slice)))); +/// assert_eq!(until_eof(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); +/// assert_eq!(until_eof("1eof2eof"), Ok(("eof2eof", "1"))); +/// assert_eq!(until_eof("eof"), Err(ErrMode::Backtrack(InputError::new("eof", ErrorKind::Slice)))); +/// ``` +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::prelude::*; +/// # use winnow::Partial; +/// use winnow::token::take_until1; +/// +/// fn until_eof(s: Partial<&str>) -> IResult<Partial<&str>, &str> { +/// take_until1("eof").parse_peek(s) +/// } +/// +/// assert_eq!(until_eof(Partial::new("hello, worldeof")), Ok((Partial::new("eof"), "hello, world"))); +/// assert_eq!(until_eof(Partial::new("hello, world")), Err(ErrMode::Incomplete(Needed::Unknown))); +/// assert_eq!(until_eof(Partial::new("hello, worldeo")), Err(ErrMode::Incomplete(Needed::Unknown))); +/// assert_eq!(until_eof(Partial::new("1eof2eof")), Ok((Partial::new("eof2eof"), "1"))); +/// assert_eq!(until_eof(Partial::new("eof")), Err(ErrMode::Backtrack(InputError::new(Partial::new("eof"), ErrorKind::Slice)))); +/// ``` +#[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, +{ + trace("take_until1", move |i: &mut I| { + if <I as StreamIsPartial>::is_partial_supported() { + take_until1_::<_, _, _, true>(i, tag.clone()) + } else { + take_until1_::<_, _, _, false>(i, tag.clone()) + } + }) +} + +fn take_until1_<T, I, Error: ParserError<I>, const PARTIAL: bool>( + i: &mut I, + t: T, +) -> PResult<<I as Stream>::Slice, Error> +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)), + } +} diff --git a/vendor/winnow/src/token/tests.rs b/vendor/winnow/src/token/tests.rs new file mode 100644 index 0000000..d9f3646 --- /dev/null +++ b/vendor/winnow/src/token/tests.rs @@ -0,0 +1,701 @@ +use super::*; + +#[cfg(feature = "std")] +use proptest::prelude::*; + +use crate::binary::length_data; +use crate::combinator::delimited; +use crate::error::ErrMode; +use crate::error::ErrorKind; +use crate::error::InputError; +use crate::error::Needed; +use crate::stream::AsChar; +use crate::token::tag; +use crate::unpeek; +use crate::IResult; +use crate::Parser; +use crate::Partial; + +#[test] +fn complete_take_while_m_n_utf8_all_matching() { + let result: IResult<&str, &str> = + take_while(1..=4, |c: char| c.is_alphabetic()).parse_peek("øn"); + assert_eq!(result, Ok(("", "øn"))); +} + +#[test] +fn complete_take_while_m_n_utf8_all_matching_substring() { + let result: IResult<&str, &str> = take_while(1, |c: char| c.is_alphabetic()).parse_peek("øn"); + assert_eq!(result, Ok(("n", "ø"))); +} + +#[cfg(feature = "std")] +fn model_complete_take_while_m_n( + m: usize, + n: usize, + valid: usize, + input: &str, +) -> IResult<&str, &str> { + if n < m { + Err(crate::error::ErrMode::from_error_kind( + &input, + crate::error::ErrorKind::Slice, + )) + } else if m <= valid { + let offset = n.min(valid); + Ok((&input[offset..], &input[0..offset])) + } else { + Err(crate::error::ErrMode::from_error_kind( + &input, + crate::error::ErrorKind::Slice, + )) + } +} + +#[cfg(feature = "std")] +proptest! { + #[test] + #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 + fn complete_take_while_m_n_bounds(m in 0..20usize, n in 0..20usize, valid in 0..20usize, invalid in 0..20usize) { + let input = format!("{:a<valid$}{:b<invalid$}", "", "", valid=valid, invalid=invalid); + let expected = model_complete_take_while_m_n(m, n, valid, &input); + if m <= n { + let actual = take_while(m..=n, |c: char| c == 'a').parse_peek(input.as_str()); + assert_eq!(expected, actual); + } + } +} + +#[test] +fn partial_any_str() { + use super::any; + assert_eq!( + any::<_, InputError<Partial<&str>>>.parse_peek(Partial::new("Ә")), + Ok((Partial::new(""), 'Ә')) + ); +} + +#[test] +fn partial_one_of_test() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> { + one_of(['a', 'b']).parse_peek(i) + } + + let a = &b"abcd"[..]; + assert_eq!(f(Partial::new(a)), Ok((Partial::new(&b"bcd"[..]), b'a'))); + + let b = &b"cde"[..]; + assert_eq!( + f(Partial::new(b)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(b), + ErrorKind::Verify + ))) + ); + + fn utf8(i: Partial<&str>) -> IResult<Partial<&str>, char> { + one_of(['+', '\u{FF0B}']).parse_peek(i) + } + + assert!(utf8(Partial::new("+")).is_ok()); + assert!(utf8(Partial::new("\u{FF0B}")).is_ok()); +} + +#[test] +fn char_byteslice() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> { + 'c'.parse_peek(i) + } + + let a = &b"abcd"[..]; + assert_eq!( + f(Partial::new(a)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(a), + ErrorKind::Verify + ))) + ); + + let b = &b"cde"[..]; + assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), b'c'))); +} + +#[test] +fn char_str() { + fn f(i: Partial<&str>) -> IResult<Partial<&str>, char> { + 'c'.parse_peek(i) + } + + let a = "abcd"; + assert_eq!( + f(Partial::new(a)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(a), + ErrorKind::Verify + ))) + ); + + let b = "cde"; + assert_eq!(f(Partial::new(b)), Ok((Partial::new("de"), 'c'))); +} + +#[test] +fn partial_none_of_test() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> { + none_of(['a', 'b']).parse_peek(i) + } + + let a = &b"abcd"[..]; + assert_eq!( + f(Partial::new(a)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(a), + ErrorKind::Verify + ))) + ); + + let b = &b"cde"[..]; + assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), b'c'))); +} + +#[test] +fn partial_is_a() { + fn a_or_b(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_while(1.., ['a', 'b']).parse_peek(i) + } + + let a = Partial::new(&b"abcd"[..]); + assert_eq!(a_or_b(a), Ok((Partial::new(&b"cd"[..]), &b"ab"[..]))); + + let b = Partial::new(&b"bcde"[..]); + assert_eq!(a_or_b(b), Ok((Partial::new(&b"cde"[..]), &b"b"[..]))); + + let c = Partial::new(&b"cdef"[..]); + assert_eq!( + a_or_b(c), + Err(ErrMode::Backtrack(error_position!(&c, ErrorKind::Slice))) + ); + + let d = Partial::new(&b"bacdef"[..]); + assert_eq!(a_or_b(d), Ok((Partial::new(&b"cdef"[..]), &b"ba"[..]))); +} + +#[test] +fn partial_is_not() { + fn a_or_b(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_till1(['a', 'b']).parse_peek(i) + } + + let a = Partial::new(&b"cdab"[..]); + assert_eq!(a_or_b(a), Ok((Partial::new(&b"ab"[..]), &b"cd"[..]))); + + let b = Partial::new(&b"cbde"[..]); + assert_eq!(a_or_b(b), Ok((Partial::new(&b"bde"[..]), &b"c"[..]))); + + let c = Partial::new(&b"abab"[..]); + assert_eq!( + a_or_b(c), + Err(ErrMode::Backtrack(error_position!(&c, ErrorKind::Slice))) + ); + + let d = Partial::new(&b"cdefba"[..]); + assert_eq!(a_or_b(d), Ok((Partial::new(&b"ba"[..]), &b"cdef"[..]))); + + let e = Partial::new(&b"e"[..]); + assert_eq!(a_or_b(e), Err(ErrMode::Incomplete(Needed::new(1)))); +} + +#[test] +fn partial_take_until_incomplete() { + fn y(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_until0("end").parse_peek(i) + } + assert_eq!( + y(Partial::new(&b"nd"[..])), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); + assert_eq!( + y(Partial::new(&b"123"[..])), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); + assert_eq!( + y(Partial::new(&b"123en"[..])), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); +} + +#[test] +fn partial_take_until_incomplete_s() { + fn ys(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_until0("end").parse_peek(i) + } + assert_eq!( + ys(Partial::new("123en")), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); +} + +#[test] +fn partial_recognize() { + 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) + } + let r = x(Partial::new(&b"<!-- abc --> aaa"[..])); + assert_eq!(r, Ok((Partial::new(&b" aaa"[..]), &b"<!-- abc -->"[..]))); + + let semicolon = &b";"[..]; + + fn ya(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + alpha.recognize().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) + } + 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) + } + 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) + } + 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) + } + 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) + } + 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) + } + let rms = yms(Partial::new(&b" \t\r\n;"[..])); + assert_eq!(rms, Ok((Partial::new(semicolon), &b" \t\r\n"[..]))); +} + +#[test] +fn partial_take_while0() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_while(0.., AsChar::is_alpha).parse_peek(i) + } + let a = &b""[..]; + let b = &b"abcd"[..]; + let c = &b"abcd123"[..]; + let d = &b"123"[..]; + + assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!(f(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!(f(Partial::new(c)), Ok((Partial::new(d), b))); + assert_eq!(f(Partial::new(d)), Ok((Partial::new(d), a))); +} + +#[test] +fn partial_take_while1() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_while(1.., AsChar::is_alpha).parse_peek(i) + } + let a = &b""[..]; + let b = &b"abcd"[..]; + let c = &b"abcd123"[..]; + let d = &b"123"[..]; + + assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!(f(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!(f(Partial::new(c)), Ok((Partial::new(&b"123"[..]), b))); + assert_eq!( + f(Partial::new(d)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(d), + ErrorKind::Slice + ))) + ); +} + +#[test] +fn partial_take_while_m_n() { + fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_while(2..=4, AsChar::is_alpha).parse_peek(i) + } + let a = &b""[..]; + let b = &b"a"[..]; + let c = &b"abc"[..]; + let d = &b"abc123"[..]; + let e = &b"abcde"[..]; + let f = &b"123"[..]; + + assert_eq!(x(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(2)))); + assert_eq!(x(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!(x(Partial::new(c)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!(x(Partial::new(d)), Ok((Partial::new(&b"123"[..]), c))); + assert_eq!( + x(Partial::new(e)), + Ok((Partial::new(&b"e"[..]), &b"abcd"[..])) + ); + assert_eq!( + x(Partial::new(f)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(f), + ErrorKind::Slice + ))) + ); +} + +#[test] +fn partial_take_till0() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_till0(AsChar::is_alpha).parse_peek(i) + } + let a = &b""[..]; + let b = &b"abcd"[..]; + let c = &b"123abcd"[..]; + let d = &b"123"[..]; + + assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!( + f(Partial::new(b)), + Ok((Partial::new(&b"abcd"[..]), &b""[..])) + ); + assert_eq!( + f(Partial::new(c)), + Ok((Partial::new(&b"abcd"[..]), &b"123"[..])) + ); + assert_eq!(f(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1)))); +} + +#[test] +fn partial_take_till1() { + fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + take_till1(AsChar::is_alpha).parse_peek(i) + } + let a = &b""[..]; + let b = &b"abcd"[..]; + let c = &b"123abcd"[..]; + let d = &b"123"[..]; + + assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1)))); + assert_eq!( + f(Partial::new(b)), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(b), + ErrorKind::Slice + ))) + ); + assert_eq!( + f(Partial::new(c)), + Ok((Partial::new(&b"abcd"[..]), &b"123"[..])) + ); + assert_eq!(f(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1)))); +} + +#[test] +fn partial_take_while_utf8() { + fn f(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(0.., |c| c != '點').parse_peek(i) + } + + assert_eq!( + f(Partial::new("")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + f(Partial::new("abcd")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!(f(Partial::new("abcd點")), Ok((Partial::new("點"), "abcd"))); + assert_eq!( + f(Partial::new("abcd點a")), + Ok((Partial::new("點a"), "abcd")) + ); + + fn g(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(0.., |c| c == '點').parse_peek(i) + } + + assert_eq!( + g(Partial::new("")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點"))); + assert_eq!( + g(Partial::new("點點點a")), + Ok((Partial::new("a"), "點點點")) + ); +} + +#[test] +fn partial_take_till0_utf8() { + fn f(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_till0(|c| c == '點').parse_peek(i) + } + + assert_eq!( + f(Partial::new("")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + f(Partial::new("abcd")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!(f(Partial::new("abcd點")), Ok((Partial::new("點"), "abcd"))); + assert_eq!( + f(Partial::new("abcd點a")), + Ok((Partial::new("點a"), "abcd")) + ); + + fn g(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_till0(|c| c != '點').parse_peek(i) + } + + assert_eq!( + g(Partial::new("")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點"))); + assert_eq!( + g(Partial::new("點點點a")), + Ok((Partial::new("a"), "點點點")) + ); +} + +#[test] +fn partial_take_utf8() { + fn f(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take(3_usize).parse_peek(i) + } + + assert_eq!( + f(Partial::new("")), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); + assert_eq!( + f(Partial::new("ab")), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); + assert_eq!( + f(Partial::new("點")), + Err(ErrMode::Incomplete(Needed::Unknown)) + ); + assert_eq!(f(Partial::new("ab點cd")), Ok((Partial::new("cd"), "ab點"))); + assert_eq!(f(Partial::new("a點bcd")), Ok((Partial::new("cd"), "a點b"))); + assert_eq!(f(Partial::new("a點b")), Ok((Partial::new(""), "a點b"))); + + fn g(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(0.., |c| c == '點').parse_peek(i) + } + + assert_eq!( + g(Partial::new("")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點"))); + assert_eq!( + g(Partial::new("點點點a")), + Ok((Partial::new("a"), "點點點")) + ); +} + +#[test] +fn partial_take_while_m_n_utf8_fixed() { + fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(1, |c| c == 'A' || c == '😃').parse_peek(i) + } + assert_eq!(parser(Partial::new("A!")), Ok((Partial::new("!"), "A"))); + assert_eq!(parser(Partial::new("😃!")), Ok((Partial::new("!"), "😃"))); +} + +#[test] +fn partial_take_while_m_n_utf8_range() { + fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(1..=2, |c| c == 'A' || c == '😃').parse_peek(i) + } + assert_eq!(parser(Partial::new("A!")), Ok((Partial::new("!"), "A"))); + assert_eq!(parser(Partial::new("😃!")), Ok((Partial::new("!"), "😃"))); +} + +#[test] +fn partial_take_while_m_n_utf8_full_match_fixed() { + fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(1, |c: char| c.is_alphabetic()).parse_peek(i) + } + assert_eq!(parser(Partial::new("øn")), Ok((Partial::new("n"), "ø"))); +} + +#[test] +fn partial_take_while_m_n_utf8_full_match_range() { + fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> { + take_while(1..=2, |c: char| c.is_alphabetic()).parse_peek(i) + } + assert_eq!(parser(Partial::new("øn")), Ok((Partial::new(""), "øn"))); +} + +#[test] +#[cfg(feature = "std")] +fn partial_recognize_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) + } + assert_eq!( + x(Partial::new(&b"ab."[..])), + Ok((Partial::new(&b"."[..]), &b"ab"[..])) + ); + assert_eq!( + y(Partial::new(&b"ab."[..])), + Ok((Partial::new(&b"."[..]), &b"ab"[..])) + ); +} + +#[test] +fn partial_length_bytes() { + use crate::binary::le_u8; + + fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + length_data(le_u8).parse_peek(i) + } + assert_eq!( + x(Partial::new(b"\x02..>>")), + Ok((Partial::new(&b">>"[..]), &b".."[..])) + ); + assert_eq!( + x(Partial::new(b"\x02..")), + Ok((Partial::new(&[][..]), &b".."[..])) + ); + assert_eq!( + x(Partial::new(b"\x02.")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + x(Partial::new(b"\x02")), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + + fn y(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + let (i, _) = "magic".parse_peek(i)?; + length_data(le_u8).parse_peek(i) + } + assert_eq!( + y(Partial::new(b"magic\x02..>>")), + Ok((Partial::new(&b">>"[..]), &b".."[..])) + ); + assert_eq!( + y(Partial::new(b"magic\x02..")), + Ok((Partial::new(&[][..]), &b".."[..])) + ); + assert_eq!( + y(Partial::new(b"magic\x02.")), + Err(ErrMode::Incomplete(Needed::new(1))) + ); + assert_eq!( + y(Partial::new(b"magic\x02")), + Err(ErrMode::Incomplete(Needed::new(2))) + ); +} + +#[cfg(feature = "alloc")] +#[test] +fn partial_case_insensitive() { + fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + tag_no_case("ABcd").parse_peek(i) + } + assert_eq!( + test(Partial::new(&b"aBCdefgh"[..])), + Ok((Partial::new(&b"efgh"[..]), &b"aBCd"[..])) + ); + assert_eq!( + test(Partial::new(&b"abcdefgh"[..])), + Ok((Partial::new(&b"efgh"[..]), &b"abcd"[..])) + ); + assert_eq!( + test(Partial::new(&b"ABCDefgh"[..])), + Ok((Partial::new(&b"efgh"[..]), &b"ABCD"[..])) + ); + assert_eq!( + test(Partial::new(&b"ab"[..])), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + test(Partial::new(&b"Hello"[..])), + Err(ErrMode::Backtrack(error_position!( + &Partial::new(&b"Hello"[..]), + ErrorKind::Tag + ))) + ); + assert_eq!( + test(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_no_case("ABcd").parse_peek(i) + } + assert_eq!( + test2(Partial::new("aBCdefgh")), + Ok((Partial::new("efgh"), "aBCd")) + ); + assert_eq!( + test2(Partial::new("abcdefgh")), + Ok((Partial::new("efgh"), "abcd")) + ); + assert_eq!( + test2(Partial::new("ABCDefgh")), + Ok((Partial::new("efgh"), "ABCD")) + ); + assert_eq!( + test2(Partial::new("ab")), + Err(ErrMode::Incomplete(Needed::new(2))) + ); + assert_eq!( + test2(Partial::new("Hello")), + Err(ErrMode::Backtrack(error_position!( + &Partial::new("Hello"), + ErrorKind::Tag + ))) + ); + assert_eq!( + test2(Partial::new("Hel")), + Err(ErrMode::Backtrack(error_position!( + &Partial::new("Hel"), + ErrorKind::Tag + ))) + ); +} + +#[test] +fn partial_tag_fixed_size_array() { + fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + tag([0x42]).parse_peek(i) + } + fn test2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> { + tag(&[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"[..]))); +} diff --git a/vendor/winnow/src/trace/internals.rs b/vendor/winnow/src/trace/internals.rs new file mode 100644 index 0000000..c38b11e --- /dev/null +++ b/vendor/winnow/src/trace/internals.rs @@ -0,0 +1,301 @@ +#![cfg(feature = "std")] + +use std::io::Write; + +use crate::error::ErrMode; +use crate::stream::Stream; +use crate::*; + +pub struct Trace<P, D, I, O, E> +where + P: Parser<I, O, E>, + I: Stream, + D: std::fmt::Display, +{ + parser: P, + name: D, + call_count: usize, + i: core::marker::PhantomData<I>, + o: core::marker::PhantomData<O>, + e: core::marker::PhantomData<E>, +} + +impl<P, D, I, O, E> Trace<P, D, I, O, E> +where + P: Parser<I, O, E>, + I: Stream, + D: std::fmt::Display, +{ + #[inline(always)] + pub fn new(parser: P, name: D) -> Self { + Self { + parser, + name, + call_count: 0, + i: Default::default(), + o: Default::default(), + e: Default::default(), + } + } +} + +impl<P, D, I, O, E> Parser<I, O, E> for Trace<P, D, I, O, E> +where + P: Parser<I, O, E>, + I: Stream, + D: std::fmt::Display, +{ + #[inline] + fn parse_next(&mut self, i: &mut I) -> PResult<O, E> { + let depth = Depth::new(); + let original = i.checkpoint(); + start(*depth, &self.name, self.call_count, i); + + let res = self.parser.parse_next(i); + + let consumed = i.offset_from(&original); + let severity = Severity::with_result(&res); + end(*depth, &self.name, self.call_count, consumed, severity); + self.call_count += 1; + + res + } +} + +pub struct Depth { + depth: usize, + inc: bool, +} + +impl Depth { + pub fn new() -> Self { + let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + let inc = true; + Self { depth, inc } + } + + pub fn existing() -> Self { + let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst); + let inc = false; + Self { depth, inc } + } +} + +impl Drop for Depth { + fn drop(&mut self) { + if self.inc { + let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst); + } + } +} + +impl AsRef<usize> for Depth { + #[inline(always)] + fn as_ref(&self) -> &usize { + &self.depth + } +} + +impl crate::lib::std::ops::Deref for Depth { + type Target = usize; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.depth + } +} + +static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + +pub enum Severity { + Success, + Backtrack, + Cut, + Incomplete, +} + +impl Severity { + pub fn with_result<T, E>(result: &Result<T, ErrMode<E>>) -> Self { + match result { + Ok(_) => Self::Success, + Err(ErrMode::Backtrack(_)) => Self::Backtrack, + Err(ErrMode::Cut(_)) => Self::Cut, + Err(ErrMode::Incomplete(_)) => Self::Incomplete, + } + } +} + +pub fn start<I: Stream>( + depth: usize, + name: &dyn crate::lib::std::fmt::Display, + count: usize, + input: &I, +) { + let gutter_style = anstyle::Style::new().bold(); + let input_style = anstyle::Style::new().underline(); + let eof_style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Cyan.into())); + + let (call_width, input_width) = column_widths(); + + let count = if 0 < count { + format!(":{count}") + } else { + "".to_owned() + }; + let call_column = format!("{:depth$}> {name}{count}", ""); + + // The debug version of `slice` might be wider, either due to rendering one byte as two nibbles or + // escaping in strings. + let mut debug_slice = format!("{:#?}", input.raw()); + let (debug_slice, eof) = if let Some(debug_offset) = debug_slice + .char_indices() + .enumerate() + .find_map(|(pos, (offset, _))| (input_width <= pos).then_some(offset)) + { + debug_slice.truncate(debug_offset); + let eof = ""; + (debug_slice, eof) + } else { + let eof = if debug_slice.chars().count() < input_width { + "∅" + } else { + "" + }; + (debug_slice, eof) + }; + + let writer = anstream::stderr(); + let mut writer = writer.lock(); + let _ = writeln!( + writer, + "{call_column:call_width$} {gutter_style}|{gutter_reset} {input_style}{debug_slice}{input_reset}{eof_style}{eof}{eof_reset}", + gutter_style=gutter_style.render(), + gutter_reset=gutter_style.render_reset(), + input_style=input_style.render(), + input_reset=input_style.render_reset(), + eof_style=eof_style.render(), + eof_reset=eof_style.render_reset(), + ); +} + +pub fn end( + depth: usize, + name: &dyn crate::lib::std::fmt::Display, + count: usize, + consumed: usize, + severity: Severity, +) { + let gutter_style = anstyle::Style::new().bold(); + + let (call_width, _) = column_widths(); + + let count = if 0 < count { + format!(":{count}") + } else { + "".to_owned() + }; + let call_column = format!("{:depth$}< {name}{count}", ""); + + let (status_style, status) = match severity { + Severity::Success => { + let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())); + let status = format!("+{}", consumed); + (style, status) + } + Severity::Backtrack => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())), + "backtrack".to_owned(), + ), + Severity::Cut => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), + "cut".to_owned(), + ), + Severity::Incomplete => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), + "incomplete".to_owned(), + ), + }; + + let writer = anstream::stderr(); + let mut writer = writer.lock(); + let _ = writeln!( + writer, + "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}", + gutter_style=gutter_style.render(), + gutter_reset=gutter_style.render_reset(), + status_style=status_style.render(), + status_reset=status_style.render_reset(), + ); +} + +pub 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(); + + let call_column = format!("{:depth$}| {name}", ""); + + let (status_style, status) = match severity { + Severity::Success => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())), + "", + ), + Severity::Backtrack => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())), + "backtrack", + ), + Severity::Cut => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), + "cut", + ), + Severity::Incomplete => ( + anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), + "incomplete", + ), + }; + + let writer = anstream::stderr(); + let mut writer = writer.lock(); + let _ = writeln!( + writer, + "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}", + gutter_style=gutter_style.render(), + gutter_reset=gutter_style.render_reset(), + status_style=status_style.render(), + status_reset=status_style.render_reset(), + ); +} + +fn column_widths() -> (usize, usize) { + let term_width = term_width(); + + let min_call_width = 40; + let min_input_width = 20; + let decor_width = 3; + let extra_width = term_width + .checked_sub(min_call_width + min_input_width + decor_width) + .unwrap_or_default(); + let call_width = min_call_width + 2 * extra_width / 3; + let input_width = min_input_width + extra_width / 3; + + (call_width, input_width) +} + +fn term_width() -> usize { + columns_env().or_else(query_width).unwrap_or(80) +} + +fn query_width() -> Option<usize> { + use is_terminal::IsTerminal; + if std::io::stderr().is_terminal() { + terminal_size::terminal_size().map(|(w, _h)| w.0.into()) + } else { + None + } +} + +fn columns_env() -> Option<usize> { + std::env::var("COLUMNS") + .ok() + .and_then(|c| c.parse::<usize>().ok()) +} diff --git a/vendor/winnow/src/trace/mod.rs b/vendor/winnow/src/trace/mod.rs new file mode 100644 index 0000000..b7d7d38 --- /dev/null +++ b/vendor/winnow/src/trace/mod.rs @@ -0,0 +1,99 @@ +//! Parser execution tracing +//! +//! By default, nothing happens and tracing gets compiled away as a no-op. To enable tracing, use +//! `--features debug`. +//! +//! # Example +//! +//!![Trace output from string example](https://raw.githubusercontent.com/winnow-rs/winnow/main/assets/trace.svg "Example output") +#![cfg_attr(feature = "debug", allow(clippy::std_instead_of_core))] + +#[cfg(feature = "debug")] +mod internals; + +use crate::error::ErrMode; +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. +/// +/// See [`trace` module][self] for more details. +/// +/// # Example +/// +/// ```rust +/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; +/// # use winnow::token::take_while; +/// # use winnow::stream::AsChar; +/// # use winnow::prelude::*; +/// use winnow::trace::trace; +/// +/// fn short_alpha<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { +/// trace("short_alpha", +/// take_while(3..=6, AsChar::is_alpha) +/// ).parse_next(s) +/// } +/// +/// assert_eq!(short_alpha.parse_peek(b"latin123"), Ok((&b"123"[..], &b"latin"[..]))); +/// assert_eq!(short_alpha.parse_peek(b"lengthy"), Ok((&b"y"[..], &b"length"[..]))); +/// assert_eq!(short_alpha.parse_peek(b"latin"), Ok((&b""[..], &b"latin"[..]))); +/// assert_eq!(short_alpha.parse_peek(b"ed"), Err(ErrMode::Backtrack(InputError::new(&b"ed"[..], ErrorKind::Slice)))); +/// assert_eq!(short_alpha.parse_peek(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice)))); +/// ``` +#[cfg_attr(not(feature = "debug"), allow(unused_variables))] +#[cfg_attr(not(feature = "debug"), allow(unused_mut))] +#[cfg_attr(not(feature = "debug"), inline(always))] +pub fn trace<I: Stream, O, E>( + name: impl crate::lib::std::fmt::Display, + parser: impl Parser<I, O, E>, +) -> impl Parser<I, O, E> { + #[cfg(feature = "debug")] + { + internals::Trace::new(parser, name) + } + #[cfg(not(feature = "debug"))] + { + parser + } +} + +#[cfg_attr(not(feature = "debug"), allow(unused_variables))] +pub(crate) fn trace_result<T, E>( + name: impl crate::lib::std::fmt::Display, + res: &Result<T, ErrMode<E>>, +) { + #[cfg(feature = "debug")] + { + let depth = internals::Depth::existing(); + let severity = internals::Severity::with_result(res); + internals::result(*depth, &name, severity); + } +} + +#[test] +#[cfg(feature = "std")] +#[cfg_attr(miri, ignore)] +#[cfg(unix)] +#[cfg(feature = "debug")] +fn example() { + use term_transcript::{test::TestConfig, ShellOptions}; + + let path = snapbox::cmd::compile_example("string", ["--features=debug"]).unwrap(); + + let current_dir = path.parent().unwrap(); + let cmd = path.file_name().unwrap(); + // HACK: term_transcript doesn't allow non-UTF8 paths + let cmd = format!("./{}", cmd.to_string_lossy()); + + TestConfig::new( + ShellOptions::default() + .with_current_dir(current_dir) + .with_env("CLICOLOR_FORCE", "1"), + ) + .test("assets/trace.svg", [cmd.as_str()]); +} |