winnow-0.6.26/.cargo_vcs_info.json0000644000000001360000000000100124500ustar { "git": { "sha1": "b9ecb11d8eabf1476030eb19b179ecddab88606f" }, "path_in_vcs": "" }winnow-0.6.26/Cargo.lock0000644000001210640000000000100104270ustar # 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 = "annotate-snippets" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "086b0afab3b349e5691143adbfb26983821e3eec4ba4c51957104d372c2e1b7d" dependencies = [ "anstyle", "unicode-width", ] [[package]] name = "anstream" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon 1.0.1", "colorchoice", "is-terminal", "utf8parse", ] [[package]] name = "anstream" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon 3.0.3", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" version = "0.2.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 = "anstyle-wincon" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", ] [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" 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 = "automod" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edf3ee19dbc0a46d740f6f0926bde8c50f02bdbc7b536842da28f6ac56513a8b" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" 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 = "is_terminal_polyfill" version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b52b2de84ed0341893ce61ca1af04fa54eea0a764ecc38c6855cc5db84dc1927" dependencies = [ "is-terminal", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 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.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 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 = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[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.60", ] [[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.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ad8c7be18cc9ec7f4d7948ad6b9df0e04fc649663e3c0ed59f304ed17ca69e9" dependencies = [ "anstream 0.6.14", "anstyle", "escargot", "normalize-line-endings", "similar", "snapbox-macros", ] [[package]] name = "snapbox-macros" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d" dependencies = [ "anstream 0.6.14", ] [[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.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 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.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix 0.38.6", "windows-sys 0.59.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.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[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.60", "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.60", "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-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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 = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.6.26" dependencies = [ "annotate-snippets", "anstream 0.3.2", "anstyle", "anyhow", "automod", "circular", "criterion", "doc-comment", "is-terminal", "lexopt", "memchr", "proptest", "rustc-hash", "snapbox", "term-transcript", "terminal_size", ] [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" winnow-0.6.26/Cargo.toml0000644000000160620000000000100104530ustar # 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.65.0" name = "winnow" version = "0.6.26" build = false include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A byte-oriented, zero-copy, parser combinators library" readme = "README.md" keywords = [ "parser", "parser-combinators", "parsing", "streaming", "bit", ] categories = ["parsing"] license = "MIT" repository = "https://github.com/winnow-rs/winnow" [package.metadata.docs.rs] features = ["unstable-doc"] rustdoc-args = [ "--cfg", "docsrs", "--generate-link-to-definition", ] [[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 = """ ## [Unreleased] - ReleaseDate """ search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ [Unreleased]: https://github.com/winnow-rs/winnow/compare/{{tag_name}}...HEAD""" search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "src/lib.rs" replace = "blob/v{{version}}/CHANGELOG.md" search = 'blob/v.+\..+\..+/CHANGELOG.md' [features] alloc = [] debug = [ "std", "dep:anstream", "dep:anstyle", "dep:is-terminal", "dep:terminal_size", ] default = ["std"] simd = ["dep:memchr"] std = [ "alloc", "memchr?/std", ] unstable-doc = [ "alloc", "std", "simd", "unstable-recover", ] unstable-recover = [] [lib] name = "winnow" path = "src/lib.rs" [[example]] name = "arithmetic" path = "examples/arithmetic/main.rs" test = true required-features = ["alloc"] [[example]] name = "css" path = "examples/css/main.rs" test = true required-features = ["alloc"] [[example]] name = "custom_error" path = "examples/custom_error.rs" test = true required-features = ["alloc"] [[example]] name = "http" path = "examples/http/main.rs" required-features = ["alloc"] [[example]] name = "ini" path = "examples/ini/main.rs" test = true required-features = ["std"] [[example]] name = "iterator" path = "examples/iterator.rs" [[example]] name = "json" path = "examples/json/main.rs" test = true required-features = ["std"] [[example]] name = "json_iterator" path = "examples/json_iterator.rs" required-features = ["std"] [[example]] name = "ndjson" path = "examples/ndjson/main.rs" test = true required-features = ["std"] [[example]] name = "s_expression" path = "examples/s_expression/main.rs" required-features = ["alloc"] [[example]] name = "string" path = "examples/string/main.rs" required-features = ["alloc"] [[bench]] name = "arithmetic" path = "examples/arithmetic/bench.rs" harness = false required-features = ["alloc"] [[bench]] name = "contains_token" path = "benches/contains_token.rs" harness = false [[bench]] name = "find_slice" path = "benches/find_slice.rs" 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 = "iter" path = "benches/iter.rs" harness = false [[bench]] name = "json" path = "examples/json/bench.rs" harness = false required-features = ["std"] [[bench]] name = "next_slice" path = "benches/next_slice.rs" harness = false [[bench]] name = "number" path = "benches/number.rs" harness = false [dependencies.anstream] version = "0.3.2" optional = true [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.4.0" optional = true [dev-dependencies.annotate-snippets] version = "0.11.3" [dev-dependencies.anyhow] version = "1.0.86" [dev-dependencies.automod] version = "1.0.14" [dev-dependencies.circular] version = "0.3.0" [dev-dependencies.criterion] version = "0.5.1" [dev-dependencies.doc-comment] version = "0.3" [dev-dependencies.lexopt] version = "0.3.0" [dev-dependencies.proptest] version = "1.2.0" [dev-dependencies.rustc-hash] version = "1.1.0" [dev-dependencies.snapbox] version = "0.6.0" features = ["examples"] [dev-dependencies.term-transcript] version = "0.2.0" [lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" result_large_err = "allow" same_functions_in_if_condition = "warn" semicolon_if_nothing_returned = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" uninlined_format_args = "warn" verbose_file_reads = "warn" wildcard_imports = "allow" zero_sized_map_values = "warn" [lints.rust] unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" [lints.rust.rust_2018_idioms] level = "warn" priority = -1 [profile.bench] lto = true codegen-units = 1 debug = 2 winnow-0.6.26/Cargo.toml.orig000064400000000000000000000132031046102023000141260ustar 00000000000000[workspace] resolver = "2" members = ["fuzz"] [workspace.package] repository = "https://github.com/winnow-rs/winnow" license = "MIT" edition = "2021" rust-version = "1.65.0" # MSRV include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*" ] [workspace.lints.rust] rust_2018_idioms = { level = "warn", priority = -1 } unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" [workspace.lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" # sometimes good to name what you are returning linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" result_large_err = "allow" same_functions_in_if_condition = "warn" # self_named_module_files = "warn" # false-positives semicolon_if_nothing_returned = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" uninlined_format_args = "warn" verbose_file_reads = "warn" wildcard_imports = "allow" zero_sized_map_values = "warn" [package] name = "winnow" version = "0.6.26" description = "A byte-oriented, zero-copy, parser combinators library" categories = ["parsing"] keywords = ["parser", "parser-combinators", "parsing", "streaming", "bit"] autoexamples = false repository.workspace = true license.workspace = true edition.workspace = true rust-version.workspace = true include.workspace = true [package.metadata.docs.rs] features = ["unstable-doc"] rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [package.metadata.release] pre-release-replacements = [ {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/winnow-rs/winnow/compare/{{tag_name}}...HEAD", exactly=1}, {file="src/lib.rs", search="blob/v.+\\..+\\..+/CHANGELOG.md", replace="blob/v{{version}}/CHANGELOG.md", exactly=1}, ] [features] default = ["std"] alloc = [] std = ["alloc", "memchr?/std"] simd = ["dep:memchr"] debug = ["std", "dep:anstream", "dep:anstyle", "dep:is-terminal", "dep:terminal_size"] unstable-recover = [] unstable-doc = ["alloc", "std", "simd", "unstable-recover"] [dependencies] anstream = { version = "0.3.2", optional = true } anstyle = { version = "1.0.1", optional = true } is-terminal = { version = "0.4.9", optional = true } memchr = { version = "2.5", optional = true, default-features = false } terminal_size = { version = "0.4.0", optional = true } [dev-dependencies] doc-comment = "0.3" proptest = "1.2.0" criterion = "0.5.1" lexopt = "0.3.0" term-transcript = "0.2.0" snapbox = { version = "0.6.0", features = ["examples"] } circular = "0.3.0" rustc-hash = "1.1.0" automod = "1.0.14" annotate-snippets = "0.11.3" anyhow = "1.0.86" [profile.bench] debug = true lto = true codegen-units = 1 [[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 = "find_slice" harness = false [[bench]] name = "iter" harness = false [[bench]] name = "next_slice" 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"] [lints] workspace = true winnow-0.6.26/LICENSE-MIT000064400000000000000000000017771046102023000127100ustar 00000000000000Permission 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. winnow-0.6.26/README.md000064400000000000000000000025701046102023000125230ustar 00000000000000# 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). winnow-0.6.26/benches/contains_token.rs000064400000000000000000000077711046102023000162470ustar 00000000000000use criterion::black_box; use winnow::combinator::alt; use winnow::combinator::repeat; use winnow::prelude::*; use winnow::token::take_till; 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 { let contains = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'][..]; repeat( 0.., alt((take_while(1.., contains), take_till(1.., contains))), ) .parse_next(input) } fn parser_array(input: &mut &str) -> PResult { let contains = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; repeat( 0.., alt((take_while(1.., contains), take_till(1.., contains))), ) .parse_next(input) } fn parser_tuple(input: &mut &str) -> PResult { let contains = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); repeat( 0.., alt((take_while(1.., contains), take_till(1.., contains))), ) .parse_next(input) } fn parser_closure_or(input: &mut &str) -> PResult { 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_till(1.., contains))), ) .parse_next(input) } fn parser_closure_matches(input: &mut &str) -> PResult { let contains = |c: char| matches!(c, '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'); repeat( 0.., alt((take_while(1.., contains), take_till(1.., 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); winnow-0.6.26/benches/find_slice.rs000064400000000000000000000027531046102023000153230ustar 00000000000000use criterion::black_box; use winnow::combinator::repeat; use winnow::prelude::*; use winnow::token::take_until; fn find_slice(c: &mut criterion::Criterion) { let empty = ""; let start_byte = "\r".repeat(100); let start_slice = "\r\n".repeat(100); let small = format!("{:>10}\r\n", "").repeat(100); let large = format!("{:>10000}\r\n", "").repeat(100); let data = [ ("empty", (empty, empty)), ("start", (&start_byte, &start_slice)), ("medium", (&small, &small)), ("large", (&large, &large)), ]; let mut group = c.benchmark_group("find_slice"); for (name, samples) in data { group.bench_with_input( criterion::BenchmarkId::new("byte", name), samples.0, |b, sample| { b.iter(|| black_box(parser_byte.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("slice", name), samples.1, |b, sample| { b.iter(|| black_box(parser_slice.parse_peek(black_box(sample)).unwrap())); }, ); } group.finish(); } fn parser_byte(input: &mut &str) -> PResult { repeat(0.., (take_until(0.., "\r"), "\r")).parse_next(input) } fn parser_slice(input: &mut &str) -> PResult { repeat(0.., (take_until(0.., "\r\n"), "\r\n")).parse_next(input) } criterion::criterion_group!(benches, find_slice); criterion::criterion_main!(benches); winnow-0.6.26/benches/iter.rs000064400000000000000000000077641046102023000141760ustar 00000000000000use 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 { 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 { 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 { 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 { 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 { 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)?; let () = 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); winnow-0.6.26/benches/next_slice.rs000064400000000000000000000075221046102023000153600ustar 00000000000000use criterion::black_box; use winnow::combinator::repeat; use winnow::prelude::*; use winnow::token::literal; use winnow::token::one_of; fn next_slice(c: &mut criterion::Criterion) { let mut group = c.benchmark_group("next_slice"); let name = "ascii"; let sample = "h".repeat(100); let sample = sample.as_str(); group.bench_with_input( criterion::BenchmarkId::new("char", name), sample, |b, sample| { b.iter(|| black_box(parser_ascii_char.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("str", name), sample, |b, sample| { b.iter(|| black_box(parser_ascii_str.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("one_of", name), sample, |b, sample| { b.iter(|| black_box(parser_ascii_one_of.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("tag_char", name), sample, |b, sample| { b.iter(|| black_box(parser_ascii_tag_char.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("tag_str", name), sample, |b, sample| { b.iter(|| black_box(parser_ascii_tag_str.parse_peek(black_box(sample)).unwrap())); }, ); let name = "utf8"; let sample = "🧑".repeat(100); let sample = sample.as_str(); group.bench_with_input( criterion::BenchmarkId::new("char", name), sample, |b, sample| { b.iter(|| black_box(parser_utf8_char.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("str", name), sample, |b, sample| { b.iter(|| black_box(parser_utf8_str.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("one_of", name), sample, |b, sample| { b.iter(|| black_box(parser_utf8_one_of.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("tag_char", name), sample, |b, sample| { b.iter(|| black_box(parser_utf8_tag_char.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("tag_str", name), sample, |b, sample| { b.iter(|| black_box(parser_utf8_tag_str.parse_peek(black_box(sample)).unwrap())); }, ); group.finish(); } fn parser_ascii_char(input: &mut &str) -> PResult { repeat(0.., 'h').parse_next(input) } fn parser_ascii_str(input: &mut &str) -> PResult { repeat(0.., "h").parse_next(input) } fn parser_ascii_one_of(input: &mut &str) -> PResult { repeat(0.., one_of('h')).parse_next(input) } fn parser_ascii_tag_char(input: &mut &str) -> PResult { repeat(0.., literal('h')).parse_next(input) } fn parser_ascii_tag_str(input: &mut &str) -> PResult { repeat(0.., literal("h")).parse_next(input) } fn parser_utf8_char(input: &mut &str) -> PResult { repeat(0.., '🧑').parse_next(input) } fn parser_utf8_str(input: &mut &str) -> PResult { repeat(0.., "🧑").parse_next(input) } fn parser_utf8_one_of(input: &mut &str) -> PResult { repeat(0.., one_of('🧑')).parse_next(input) } fn parser_utf8_tag_char(input: &mut &str) -> PResult { repeat(0.., literal('🧑')).parse_next(input) } fn parser_utf8_tag_str(input: &mut &str) -> PResult { repeat(0.., literal("🧑")).parse_next(input) } criterion::criterion_group!(benches, next_slice); criterion::criterion_main!(benches); winnow-0.6.26/benches/number.rs000064400000000000000000000034371046102023000145140ustar 00000000000000#[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 { 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 { 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); winnow-0.6.26/examples/arithmetic/bench.rs000064400000000000000000000017261046102023000166420ustar 00000000000000mod 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); winnow-0.6.26/examples/arithmetic/main.rs000064400000000000000000000040651046102023000165060ustar 00000000000000use 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, implementation: Impl, } enum Impl { Eval, Ast, Lexer, } impl Default for Impl { fn default() -> Self { Self::Eval } } impl Args { fn parse() -> Result { 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) } } winnow-0.6.26/examples/arithmetic/parser.rs000064400000000000000000000066301046102023000170560ustar 00000000000000use std::str::FromStr; use winnow::prelude::*; use winnow::{ ascii::{digit1 as digits, multispace0 as multispaces}, combinator::alt, combinator::delimited, combinator::repeat, token::one_of, }; // Parser definition pub(crate) fn expr(i: &mut &str) -> PResult { let init = term.parse_next(i)?; repeat(0.., (one_of(['+', '-']), term)) .fold( 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 { let init = factor.parse_next(i)?; repeat(0.., (one_of(['*', '/']), factor)) .fold( 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 whitespace // 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 { delimited( multispaces, alt((digits.try_map(FromStr::from_str), parens)), multispaces, ) .parse_next(i) } // We parse any expr surrounded by parens, ignoring all whitespace around those fn parens(i: &mut &str) -> PResult { 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); } winnow-0.6.26/examples/arithmetic/parser_ast.rs000064400000000000000000000130261046102023000177220ustar 00000000000000use 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::repeat, token::one_of, }; #[derive(Debug, Clone)] pub(crate) enum Expr { Value(i64), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), Paren(Box), } impl Expr { pub(crate) 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(crate) fn expr(i: &mut &str) -> PResult { let init = term.parse_next(i)?; repeat(0.., (one_of(['+', '-']), term)) .fold( 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 { let init = factor.parse_next(i)?; repeat(0.., (one_of(['*', '/']), factor)) .fold( 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 { delimited( multispaces, alt((digits.try_map(FromStr::from_str).map(Expr::Value), parens)), multispaces, ) .parse_next(i) } fn parens(i: &mut &str) -> PResult { 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); } winnow-0.6.26/examples/arithmetic/parser_lexer.rs000064400000000000000000000215421046102023000202540ustar 00000000000000use 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::peek, combinator::repeat, combinator::{delimited, preceded, terminated}, token::any, token::one_of, }; #[derive(Debug, Clone)] pub(crate) enum Expr { Value(i64), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), Paren(Box), } impl Expr { pub(crate) 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 for Token { #[inline(always)] fn contains_token(&self, token: Token) -> bool { *self == token } } impl winnow::stream::ContainsToken for &'_ [Token] { #[inline] fn contains_token(&self, token: Token) -> bool { self.iter().any(|t| *t == token) } } impl winnow::stream::ContainsToken for &'_ [Token; LEN] { #[inline] fn contains_token(&self, token: Token) -> bool { self.iter().any(|t| *t == token) } } impl winnow::stream::ContainsToken for [Token; LEN] { #[inline] fn contains_token(&self, token: Token) -> bool { self.iter().any(|t| *t == token) } } #[allow(dead_code)] pub(crate) fn expr2(i: &mut &str) -> PResult { let tokens = lex.parse_next(i)?; expr.parse_next(&mut tokens.as_slice()) } pub(crate) fn lex(i: &mut &str) -> PResult> { preceded(multispaces, repeat(1.., terminated(token, multispaces))).parse_next(i) } fn token(i: &mut &str) -> PResult { 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(crate) fn expr(i: &mut &[Token]) -> PResult { let init = term.parse_next(i)?; repeat( 0.., ( one_of([Token::Oper(Oper::Add), Token::Oper(Oper::Sub)]), term, ), ) .fold( 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 { let init = factor.parse_next(i)?; repeat( 0.., ( one_of([Token::Oper(Oper::Mul), Token::Oper(Oper::Div)]), factor, ), ) .fold( 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 { 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 { 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); } winnow-0.6.26/examples/css/main.rs000064400000000000000000000022511046102023000151400ustar 00000000000000use 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, } impl Args { fn parse() -> Result { 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, } )) ); } winnow-0.6.26/examples/css/parser.rs000064400000000000000000000014661046102023000155170ustar 00000000000000use winnow::combinator::seq; use winnow::prelude::*; use winnow::token::take_while; #[derive(Debug, Eq, PartialEq)] pub(crate) struct Color { pub(crate) red: u8, pub(crate) green: u8, pub(crate) blue: u8, } impl std::str::FromStr for Color { // The error must be owned type Err = String; fn from_str(s: &str) -> Result { hex_color.parse(s).map_err(|e| e.to_string()) } } pub(crate) fn hex_color(input: &mut &str) -> PResult { seq!(Color { _: '#', red: hex_primary, green: hex_primary, blue: hex_primary }) .parse_next(input) } fn hex_primary(input: &mut &str) -> PResult { take_while(2, |c: char| c.is_ascii_hexdigit()) .try_map(|input| u8::from_str_radix(input, 16)) .parse_next(input) } winnow-0.6.26/examples/custom_error.rs000064400000000000000000000031611046102023000161500ustar 00000000000000use winnow::error::AddContext; use winnow::error::ErrMode; use winnow::error::ErrorKind; use winnow::error::FromExternalError; use winnow::error::ParserError; use winnow::prelude::*; use winnow::stream::Stream; #[derive(Debug)] pub enum CustomError { MyError, Winnow(I, ErrorKind), External { cause: Box, input: I, kind: ErrorKind, }, } impl ParserError for CustomError { fn from_error_kind(input: &I, kind: ErrorKind) -> Self { CustomError::Winnow(input.clone(), kind) } fn append(self, _: &I, _: &::Checkpoint, _: ErrorKind) -> Self { self } } impl AddContext for CustomError { #[inline] fn add_context( self, _input: &I, _token_start: &::Checkpoint, _context: C, ) -> Self { self } } impl FromExternalError for CustomError { #[inline] fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self { CustomError::External { cause: Box::new(e), input: input.clone(), kind, } } } pub fn parse<'s>(_input: &mut &'s str) -> PResult<&'s str, CustomError<&'s str>> { Err(ErrMode::Backtrack(CustomError::MyError)) } fn main() {} #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let err = parse.parse_next(&mut "").unwrap_err(); assert!(matches!(err, ErrMode::Backtrack(CustomError::MyError))); } } winnow-0.6.26/examples/http/bench.rs000064400000000000000000000020211046102023000154550ustar 00000000000000mod 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); winnow-0.6.26/examples/http/main.rs000064400000000000000000000020741046102023000153320ustar 00000000000000mod 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, } impl Args { fn parse() -> Result { 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) } } winnow-0.6.26/examples/http/parser.rs000064400000000000000000000061471046102023000157070ustar 00000000000000use winnow::combinator::seq; use winnow::prelude::*; use winnow::{ascii::line_ending, combinator::repeat, token::take_while}; pub(crate) type Stream<'i> = &'i [u8]; #[rustfmt::skip] #[derive(Debug)] #[allow(dead_code)] pub(crate) struct Request<'a> { method: &'a [u8], uri: &'a [u8], version: &'a [u8], } #[derive(Debug)] #[allow(dead_code)] pub(crate) struct Header<'a> { name: &'a [u8], value: Vec<&'a [u8]>, } pub(crate) fn parse(data: &[u8]) -> Option, Vec>)>> { 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>)> { 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> { seq!( Request { method: take_while(1.., is_token), _: take_while(1.., is_space), uri: take_while(1.., is_not_space), _: take_while(1.., is_space), version: http_version, _: line_ending, }) .parse_next(input) } 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.., till_line_ending).parse_next(input)?; let _ = line_ending.parse_next(input)?; Ok(data) } fn message_header<'s>(input: &mut Stream<'s>) -> PResult> { seq!(Header { name: take_while(1.., is_token), _: ':', value: repeat(1.., message_header_value), }) .parse_next(input) } #[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 till_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' } winnow-0.6.26/examples/http/parser_streaming.rs000064400000000000000000000062121046102023000177510ustar 00000000000000use winnow::combinator::seq; use winnow::{ ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while, }; pub(crate) type Stream<'i> = Partial<&'i [u8]>; #[rustfmt::skip] #[derive(Debug)] #[allow(dead_code)] pub(crate) struct Request<'a> { method: &'a [u8], uri: &'a [u8], version: &'a [u8], } #[derive(Debug)] #[allow(dead_code)] pub(crate) struct Header<'a> { name: &'a [u8], value: Vec<&'a [u8]>, } pub(crate) fn parse(data: &[u8]) -> Option, Vec>)>> { 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>)> { 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> { seq!( Request { method: take_while(1.., is_token), _: take_while(1.., is_space), uri: take_while(1.., is_not_space), _: take_while(1.., is_space), version: http_version, _: line_ending, }) .parse_next(input) } 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.., till_line_ending).parse_next(input)?; let _ = line_ending.parse_next(input)?; Ok(data) } fn message_header<'s>(input: &mut Stream<'s>) -> PResult> { seq!(Header { name: take_while(1.., is_token), _: ':', value: repeat(1.., message_header_value), }) .parse_next(input) } #[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 till_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' } winnow-0.6.26/examples/ini/bench.rs000064400000000000000000000033671046102023000152730ustar 00000000000000use 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> { 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); winnow-0.6.26/examples/ini/main.rs000064400000000000000000000024611046102023000151320ustar 00000000000000use 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, binary: bool, } impl Args { fn parse() -> Result { 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) } } winnow-0.6.26/examples/ini/parser.rs000064400000000000000000000076371046102023000155140ustar 00000000000000use 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(crate) type Stream<'i> = &'i [u8]; pub(crate) fn categories<'s>( i: &mut Stream<'s>, ) -> PResult>> { 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(crate) fn key_value<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, &'s str)> { let key = alphanumeric.try_map(str::from_utf8).parse_next(i)?; let _ = (opt(space), '=', opt(space)).parse_next(i)?; let val = take_while(0.., |c| c != b'\n' && c != b';') .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))); } winnow-0.6.26/examples/ini/parser_str.rs000064400000000000000000000125251046102023000163740ustar 00000000000000use std::collections::HashMap; use winnow::prelude::*; use winnow::{ ascii::{alphanumeric1 as alphanumeric, space0 as space}, combinator::opt, combinator::repeat, combinator::{delimited, terminated}, token::{take_till, take_while}, }; pub(crate) type Stream<'i> = &'i str; pub(crate) fn categories<'s>( input: &mut Stream<'s>, ) -> PResult>> { 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> { 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_till(0.., is_line_ending_or_comment).parse_next(i)?; let _ = opt(space).parse_next(i)?; let _ = opt((";", till_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 till_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: {i} | o: {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: {i} | o: ({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: {i} | o: ({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: {i} | o: ({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: {i} | o: {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: {i} | o: {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: {i} | o: {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))); } winnow-0.6.26/examples/iterator.rs000064400000000000000000000045451046102023000152650ustar 00000000000000use 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::>(); 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 {res:?}, remaining input is '{remaining_input}'"); } winnow-0.6.26/examples/json/bench.rs000064400000000000000000000040621046102023000154560ustar 00000000000000use winnow::prelude::*; use winnow::Partial; mod json; mod parser_alt; 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("dispatch", name), &len, |b, _| { type Error = winnow::error::ContextError; b.iter(|| parser_dispatch::json::.parse_peek(sample).unwrap()); }, ); group.bench_with_input( criterion::BenchmarkId::new("empty-error", name), &len, |b, _| { type Error<'i> = winnow::error::EmptyError; b.iter(|| { parser_dispatch::json::> .parse_peek(sample) .unwrap() }); }, ); group.bench_with_input(criterion::BenchmarkId::new("alt", name), &len, |b, _| { type Error = winnow::error::ContextError; b.iter(|| parser_alt::json::.parse_peek(sample).unwrap()); }); group.bench_with_input( criterion::BenchmarkId::new("streaming", name), &len, |b, _| { type Error = winnow::error::ContextError; b.iter(|| { parser_partial::json:: .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); winnow-0.6.26/examples/json/json.rs000064400000000000000000000003401046102023000153430ustar 00000000000000use std::collections::HashMap; #[derive(Debug, PartialEq, Clone)] pub(crate) enum JsonValue { Null, Boolean(bool), Str(String), Num(f64), Array(Vec), Object(HashMap), } winnow-0.6.26/examples/json/main.rs000064400000000000000000000043661046102023000153320ustar 00000000000000mod json; mod parser_alt; 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_alt::json::.parse(data), Impl::Dispatch => parser_dispatch::json::.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, invalid: bool, pretty: bool, implementation: Impl, } enum Impl { Naive, Dispatch, } impl Default for Impl { fn default() -> Self { Self::Naive } } impl Args { fn parse() -> Result { 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) } } winnow-0.6.26/examples/json/parser_alt.rs000064400000000000000000000257131046102023000165410ustar 00000000000000use 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::{repeat, separated}, error::{AddContext, ParserError, StrContext}, token::{any, none_of, take, take_while}, }; use crate::json::JsonValue; pub(crate) type Stream<'i> = &'i str; /// The root element of a JSON parser is any value /// /// A parser has the following signature: /// `&mut Stream -> PResult`, with `PResult` defined as: /// `type PResult = Result>;` /// /// 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(crate) fn json<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { // `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) } /// `literal(string)` generates a parser that takes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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>>(input: &mut Stream<'i>) -> PResult { // 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 take the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { 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( repeat(0.., character).fold(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(StrContext::Expected("string".into())) .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>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { take(4usize) .verify_map(|s| u16::from_str_radix(s, 16).ok()) .parse_next(input) } /// Some combinators, like `separated` or `repeat`, 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated( separated(0.., json_value, (ws, ',', ws)), (ws, ']'), )), ) .context(StrContext::Expected("array".into())) .parse_next(input) } fn object<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated( separated(0.., key_value, (ws, ',', ws)), (ws, '}'), )), ) .context(StrContext::Expected("object".into())) .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, StrContext>>( 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>>(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(unused_imports)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error = winnow::error::ContextError; #[test] fn json_string() { assert_eq!(string::.parse_peek("\"\""), Ok(("", "".to_owned()))); assert_eq!( string::.parse_peek("\"abc\""), Ok(("", "abc".to_owned())) ); assert_eq!( string:: .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""), Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())), ); assert_eq!( string::.parse_peek("\"\\uD83D\\uDE10\""), Ok(("", "😐".to_owned())) ); assert!(string::.parse_peek("\"").is_err()); assert!(string::.parse_peek("\"abc").is_err()); assert!(string::.parse_peek("\"\\\"").is_err()); assert!(string::.parse_peek("\"\\u123\"").is_err()); assert!(string::.parse_peek("\"\\uD800\"").is_err()); assert!(string::.parse_peek("\"\\uD800\\uD800\"").is_err()); assert!(string::.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_owned(), Num(42.0)), ("b".to_owned(), Str("x".to_owned())), ] .into_iter() .collect(), ); assert_eq!(json::.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_owned())]); assert_eq!(json::.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::.parse_peek(input), Ok(( "", Object( vec![ ("null".to_owned(), Null), ("true".to_owned(), Boolean(true)), ("false".to_owned(), Boolean(false)), ("number".to_owned(), Num(123e4)), ("string".to_owned(), Str(" abc 123 ".to_owned())), ( "array".to_owned(), Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())]) ), ( "object".to_owned(), Object( vec![ ("a".to_owned(), Num(1.0)), ("b".to_owned(), Str("c".to_owned())), ] .into_iter() .collect() ) ), ("empty_array".to_owned(), Array(vec![]),), ("empty_object".to_owned(), Object(HashMap::new()),), ] .into_iter() .collect() ) )) ); } } winnow-0.6.26/examples/json/parser_dispatch.rs000064400000000000000000000256501046102023000175600ustar 00000000000000use std::collections::HashMap; use std::str; use winnow::prelude::*; use winnow::{ ascii::float, combinator::empty, combinator::fail, combinator::peek, combinator::{alt, dispatch}, combinator::{delimited, preceded, separated_pair, terminated}, combinator::{repeat, separated}, error::{AddContext, ParserError, StrContext}, token::{any, none_of, take, take_while}, }; use crate::json::JsonValue; pub(crate) type Stream<'i> = &'i str; /// The root element of a JSON parser is any value /// /// A parser has the following signature: /// `&mut Stream -> PResult`, with `PResult` defined as: /// `type PResult = Result>;` /// /// 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(crate) fn json<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { // `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) } /// `literal(string)` generates a parser that takes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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>>(input: &mut Stream<'i>) -> PResult { // 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>>(input: &mut Stream<'i>) -> PResult { // 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 take the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { preceded( '\"', terminated( repeat(0.., character).fold(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(StrContext::Expected("string".into())) .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>>(input: &mut Stream<'i>) -> PResult { let c = none_of('\"').parse_next(input)?; if c == '\\' { dispatch!(any; '"' => empty.value('"'), '\\' => empty.value('\\'), '/' => empty.value('/'), 'b' => empty.value('\x08'), 'f' => empty.value('\x0C'), 'n' => empty.value('\n'), 'r' => empty.value('\r'), 't' => empty.value('\t'), 'u' => unicode_escape, _ => fail, ) .parse_next(input) } else { Ok(c) } } fn unicode_escape<'i, E: ParserError>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { take(4usize) .verify_map(|s| u16::from_str_radix(s, 16).ok()) .parse_next(input) } /// Some combinators, like `separated` or `repeat`, 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), terminated(separated(0.., json_value, (ws, ',', ws)), (ws, ']')), ) .context(StrContext::Expected("array".into())) .parse_next(input) } fn object<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), terminated(separated(0.., key_value, (ws, ',', ws)), (ws, '}')), ) .context(StrContext::Expected("object".into())) .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult<(String, JsonValue), E> { separated_pair(string, (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>>(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(unused_imports)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error = winnow::error::ContextError; #[test] fn json_string() { assert_eq!(string::.parse_peek("\"\""), Ok(("", "".to_owned()))); assert_eq!( string::.parse_peek("\"abc\""), Ok(("", "abc".to_owned())) ); assert_eq!( string:: .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""), Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_owned())), ); assert_eq!( string::.parse_peek("\"\\uD83D\\uDE10\""), Ok(("", "😐".to_owned())) ); assert!(string::.parse_peek("\"").is_err()); assert!(string::.parse_peek("\"abc").is_err()); assert!(string::.parse_peek("\"\\\"").is_err()); assert!(string::.parse_peek("\"\\u123\"").is_err()); assert!(string::.parse_peek("\"\\uD800\"").is_err()); assert!(string::.parse_peek("\"\\uD800\\uD800\"").is_err()); assert!(string::.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_owned(), Num(42.0)), ("b".to_owned(), Str("x".to_owned())), ] .into_iter() .collect(), ); assert_eq!(json::.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_owned())]); assert_eq!(json::.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::.parse_peek(input), Ok(( "", Object( vec![ ("null".to_owned(), Null), ("true".to_owned(), Boolean(true)), ("false".to_owned(), Boolean(false)), ("number".to_owned(), Num(123e4)), ("string".to_owned(), Str(" abc 123 ".to_owned())), ( "array".to_owned(), Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())]) ), ( "object".to_owned(), Object( vec![ ("a".to_owned(), Num(1.0)), ("b".to_owned(), Str("c".to_owned())), ] .into_iter() .collect() ) ), ("empty_array".to_owned(), Array(vec![]),), ("empty_object".to_owned(), Object(HashMap::new()),), ] .into_iter() .collect() ) )) ); } } winnow-0.6.26/examples/json/parser_partial.rs000064400000000000000000000273361046102023000174200ustar 00000000000000use 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::{repeat, separated}, error::{AddContext, ParserError, StrContext}, stream::Partial, token::{any, none_of, rest, take, take_while}, }; use crate::json::JsonValue; pub(crate) type Stream<'i> = Partial<&'i str>; /// The root element of a JSON parser is any value /// /// A parser has the following signature: /// `&mut Stream -> PResult`, with `PResult` defined as: /// `type PResult = Result>;` /// /// 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(crate) fn json<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { // `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) } /// `literal(string)` generates a parser that takes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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>>(input: &mut Stream<'i>) -> PResult { // 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 take the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { 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( repeat(0.., character).fold(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(StrContext::Expected("string".into())) .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>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { take(4usize) .verify_map(|s| u16::from_str_radix(s, 16).ok()) .parse_next(input) } /// Some combinators, like `separated` or `repeat`, 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated( separated(0.., json_value, (ws, ',', ws)), (ws, ']'), )), ) .context(StrContext::Expected("array".into())) .parse_next(input) } fn object<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated( separated(0.., key_value, (ws, ',', ws)), (ws, '}'), )), ) .context(StrContext::Expected("object".into())) .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, StrContext>>( 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>>(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>>(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(unused_imports)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error = winnow::error::ContextError; #[test] fn json_string() { assert_eq!( string::.parse_peek(Partial::new("\"\"")), Ok((Partial::new(""), "".to_owned())) ); assert_eq!( string::.parse_peek(Partial::new("\"abc\"")), Ok((Partial::new(""), "abc".to_owned())) ); assert_eq!( string::.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_owned() )), ); assert_eq!( string::.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")), Ok((Partial::new(""), "😐".to_owned())) ); assert!(string::.parse_peek(Partial::new("\"")).is_err()); assert!(string::.parse_peek(Partial::new("\"abc")).is_err()); assert!(string::.parse_peek(Partial::new("\"\\\"")).is_err()); assert!(string:: .parse_peek(Partial::new("\"\\u123\"")) .is_err()); assert!(string:: .parse_peek(Partial::new("\"\\uD800\"")) .is_err()); assert!(string:: .parse_peek(Partial::new("\"\\uD800\\uD800\"")) .is_err()); assert!(string:: .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_owned(), Num(42.0)), ("b".to_owned(), Str("x".to_owned())), ] .into_iter() .collect(), ); assert_eq!( json::.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_owned())]); assert_eq!( json::.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::.parse_peek(Partial::new(input)), Ok(( Partial::new(""), Object( vec![ ("null".to_owned(), Null), ("true".to_owned(), Boolean(true)), ("false".to_owned(), Boolean(false)), ("number".to_owned(), Num(123e4)), ("string".to_owned(), Str(" abc 123 ".to_owned())), ( "array".to_owned(), Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())]) ), ( "object".to_owned(), Object( vec![ ("a".to_owned(), Num(1.0)), ("b".to_owned(), Str("c".to_owned())), ] .into_iter() .collect() ) ), ("empty_array".to_owned(), Array(vec![]),), ("empty_object".to_owned(), Object(HashMap::new()),), ] .into_iter() .collect() ) )) ); } } winnow-0.6.26/examples/json_iterator.rs000064400000000000000000000224461046102023000163160ustar 00000000000000use std::collections::HashMap; use winnow::prelude::*; use winnow::{ ascii::{alphanumeric1 as alphanumeric, float, take_escaped}, combinator::alt, combinator::cut_err, combinator::separated, combinator::{preceded, separated_pair, terminated}, error::ParserError, error::StrContext, stream::Offset, token::one_of, token::{literal, take_while}, }; use std::cell::Cell; use std::str; #[derive(Clone, Debug)] pub struct JsonValue<'a, 'b> { input: &'a str, pub offset: &'b Cell, } impl<'a, 'b: 'a> JsonValue<'a, 'b> { pub fn new(input: &'a str, offset: &'b Cell) -> 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 { 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 { 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>> { println!("array()"); let mut data = self.data(); match literal::<_, _, ()>("[").parse_next(&mut data) { Err(_) => None, Ok(_) => { println!("["); self.offset(data); let mut first = true; let mut done = false; let mut previous = 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 literal::<_, _, ()>("]").parse_next(&mut data).is_ok() { println!("]"); v.offset(data); done = true; return None; } if first { first = false; } else { match literal::<_, _, ()>(",").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)>> { println!("object()"); let mut data = self.data(); match literal::<_, _, ()>("{").parse_next(&mut data) { Err(_) => None, Ok(_) => { self.offset(data); println!("{{"); let mut first = true; let mut done = false; let mut previous = 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 literal::<_, _, ()>("}").parse_next(&mut data).is_ok() { println!("}}"); v.offset(data); done = true; return None; } if first { first = false; } else { match literal::<_, _, ()>(",").parse_next(&mut data) { Ok(_) => { println!(","); v.offset(data); } Err(_) => { done = true; return None; } } } match string(&mut data) { Ok(key) => { v.offset(data); match literal::<_, _, ()>(":").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> { take_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 { alt(("false".map(|_| false), "true".map(|_| true))).parse_next(input) } fn array(i: &mut &str) -> PResult<()> { preceded( '[', cut_err(terminated( separated(0.., 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( separated(0.., 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:?}"); } }; } winnow-0.6.26/examples/ndjson/example.ndjson000064400000000000000000003126521046102023000172320ustar 00000000000000{"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} winnow-0.6.26/examples/ndjson/main.rs000064400000000000000000000077401046102023000156530ustar 00000000000000mod parser; use std::io::Read; use winnow::error::ContextError; use winnow::error::ErrMode; use winnow::error::Needed; use winnow::prelude::*; use winnow::stream::Offset; use winnow::stream::Stream as _; fn main() -> Result<(), lexopt::Error> { let args = Args::parse()?; let input = args.input.ok_or_else(|| lexopt::Error::MissingValue { option: Some("".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 mut input = parser::Stream::new(std::str::from_utf8(buffer.data()).map_err(to_lexopt)?); let start = input.checkpoint(); match parser::ndjson::.parse_next(&mut input) { Ok(value) => { println!("{value:?}"); println!(); // Tell the buffer how much we read let consumed = input.offset_from(&start); 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, } impl Args { fn parse() -> Result { 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()) } winnow-0.6.26/examples/ndjson/parser.rs000064400000000000000000000266421046102023000162250ustar 00000000000000use 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::{repeat, separated}, error::{AddContext, ParserError, StrContext}, stream::Partial, token::{any, none_of, take, take_while}, }; #[derive(Debug, PartialEq, Clone)] pub(crate) enum JsonValue { Null, Boolean(bool), Str(String), Num(f64), Array(Vec), Object(HashMap), } /// Use `Partial` to cause `ErrMode::Incomplete` while parsing pub(crate) type Stream<'i> = Partial<&'i str>; pub(crate) fn ndjson<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { // `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) } /// `literal(string)` generates a parser that takes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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>>(input: &mut Stream<'i>) -> PResult { // 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 take the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult { 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( repeat(0.., character).fold(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(StrContext::Expected("string".into())) .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>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { 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>>(input: &mut Stream<'i>) -> PResult { take(4usize) .verify_map(|s| u16::from_str_radix(s, 16).ok()) .parse_next(input) } /// Some combinators, like `separated` or `repeat`, 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> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated( separated(0.., json_value, (ws, ',', ws)), (ws, ']'), )), ) .context(StrContext::Expected("array".into())) .parse_next(input) } fn object<'i, E: ParserError> + AddContext, StrContext>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated( separated(0.., key_value, (ws, ',', ws)), (ws, '}'), )), ) .context(StrContext::Expected("object".into())) .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, StrContext>>( 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>>(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(unused_imports)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error = winnow::error::ContextError; #[test] fn json_string() { assert_eq!( string::.parse_peek(Partial::new("\"\"")), Ok((Partial::new(""), "".to_owned())) ); assert_eq!( string::.parse_peek(Partial::new("\"abc\"")), Ok((Partial::new(""), "abc".to_owned())) ); assert_eq!( string::.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_owned() )), ); assert_eq!( string::.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")), Ok((Partial::new(""), "😐".to_owned())) ); assert!(string::.parse_peek(Partial::new("\"")).is_err()); assert!(string::.parse_peek(Partial::new("\"abc")).is_err()); assert!(string::.parse_peek(Partial::new("\"\\\"")).is_err()); assert!(string:: .parse_peek(Partial::new("\"\\u123\"")) .is_err()); assert!(string:: .parse_peek(Partial::new("\"\\uD800\"")) .is_err()); assert!(string:: .parse_peek(Partial::new("\"\\uD800\\uD800\"")) .is_err()); assert!(string:: .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_owned(), Num(42.0)), ("b".to_owned(), Str("x".to_owned())), ] .into_iter() .collect(), ); assert_eq!( ndjson::.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_owned())]); assert_eq!( ndjson::.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::.parse_peek(Partial::new(input)), Ok(( Partial::new(""), Some(Object( vec![ ("null".to_owned(), Null), ("true".to_owned(), Boolean(true)), ("false".to_owned(), Boolean(false)), ("number".to_owned(), Num(123e4)), ("string".to_owned(), Str(" abc 123 ".to_owned())), ( "array".to_owned(), Array(vec![Boolean(false), Num(1.0), Str("two".to_owned())]) ), ( "object".to_owned(), Object( vec![ ("a".to_owned(), Num(1.0)), ("b".to_owned(), Str("c".to_owned())), ] .into_iter() .collect() ) ), ("empty_array".to_owned(), Array(vec![]),), ("empty_object".to_owned(), Object(HashMap::new()),), ] .into_iter() .collect() )) )) ); } } winnow-0.6.26/examples/s_expression/main.rs000064400000000000000000000010631046102023000170710ustar 00000000000000//! 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) ); } winnow-0.6.26/examples/s_expression/parser.rs000064400000000000000000000325301046102023000174440ustar 00000000000000//! 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(crate) fn eval_from_str(src: &str) -> Result { parse_expr .parse(src) .map_err(|e| e.to_string()) .and_then(|exp| eval_expression(exp).ok_or_else(|| "Eval failed".to_owned())) } /// For parsing, we start by defining the types that define the shape of data that we want. /// In this case, we want something tree-like /// /// The remaining half is Lists. We implement these as recursive Expressions. /// For a list of numbers, we have `'(1 2 3)`, which we'll parse to: /// ``` /// 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(crate) enum Expr { Constant(Atom), /// (func-name arg1 arg2) Application(Box, Vec), /// (if predicate do-this) If(Box, Box), /// (if predicate do-this otherwise-do-this) IfElse(Box, Box, Box), /// '(3 (if (+ 3 3) 4 5) 7) Quote(Vec), } /// 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(crate) 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(crate) 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 { 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 { 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 { 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 { alt(( digit1.try_map(|digit_str: &str| digit_str.parse::().map(Atom::Num)), preceded("-", digit1).map(|digit_str: &str| Atom::Num(-digit_str.parse::().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 { alt(( "#t".map(|_| Atom::Boolean(true)), "#f".map(|_| Atom::Boolean(false)), )) .parse_next(i) } fn parse_builtin(i: &mut &'_ str) -> PResult { // 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 { // 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 { preceded(":", cut_err(alpha1)) .context(StrContext::Label("keyword")) .map(|sym_str: &str| Atom::Keyword(sym_str.to_owned())) .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, Vec)`, 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 { 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 { 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 { // 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 { 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::>>()?; 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::>>()? .into_iter() .sum(), ), BuiltIn::Times => Atom::Num( reduced_tail .into_iter() .map(get_num_from_expr) .collect::>>()? .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::>>()? .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::>>()? .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 { if let Expr::Constant(Atom::Num(n)) = e { Some(n) } else { None } } fn get_bool_from_expr(e: Expr) -> Option { if let Expr::Constant(Atom::Boolean(b)) = e { Some(b) } else { None } } winnow-0.6.26/examples/string/main.rs000064400000000000000000000036641046102023000156670ustar 00000000000000//! 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, } impl Args { fn parse() -> Result { 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")) ); } winnow-0.6.26/examples/string/parser.rs000064400000000000000000000154441046102023000162360ustar 00000000000000//! 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::repeat; use winnow::combinator::{delimited, preceded}; use winnow::error::{FromExternalError, ParserError}; use winnow::prelude::*; use winnow::token::{take_till, take_while}; /// Parse a string. Use a loop of `parse_fragment` and push all of the fragments /// into an output string. pub(crate) fn parse_string<'a, E>(input: &mut &'a str) -> PResult where E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, { // Repeat::fold 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 = repeat( 0.., // Our parser function – parses a single string fragment parse_fragment, ) .fold( // 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 Repeat::fold), 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, 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_till` parses a string of 0 or more characters that aren't one of the // given characters. let not_quote_slash = take_till(1.., ['"', '\\']); // `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_till // 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 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 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) } winnow-0.6.26/src/_topic/arithmetic.rs000064400000000000000000000005661046102023000160120ustar 00000000000000//! # 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")] //! ``` winnow-0.6.26/src/_topic/error.rs000064400000000000000000000026271046102023000150120ustar 00000000000000//! # Custom Errors //! //! A lot can be accomplished with the built-in error tools, like: //! - [`ContextError`] //! - [`Parser::context`] //! - [`cut_err`] //! //! *(see [tutorial][chapter_7])* //! //! Most other needs can likely be met by using a custom context type with [`ContextError`] instead //! of [`StrContext`]. //! This will require implementing a custom renderer. //! //! ## `ParserError` Trait //! //! When needed, you can also create your own type that implements [`ParserError`]. //! //! Optional traits include: //! - [`AddContext`] //! - [`FromExternalError`] //! - [`ErrorConvert`] //! //! There are multiple strategies for implementing support for [`AddContext`] and [`FromExternalError`]: //! - Make your error type generic over the context or external error //! - Require a trait for the context or external error and `Box` it //! - Make the context an enum like [`StrContext`] //! - Implement the trait multiple times, one for each concrete context or external error type, //! allowing custom behavior per type //! //! Example: //!```rust #![doc = include_str!("../../examples/custom_error.rs")] //!``` #![allow(unused_imports)] use crate::combinator::cut_err; use crate::error::ContextError; use crate::error::ErrorConvert; use crate::error::StrContext; use crate::Parser; use crate::_tutorial::chapter_7; use crate::error::AddContext; use crate::error::FromExternalError; use crate::error::ParserError; winnow-0.6.26/src/_topic/fromstr.rs000064400000000000000000000003251046102023000153460ustar 00000000000000//! # 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")] //! ``` winnow-0.6.26/src/_topic/http.rs000064400000000000000000000001331046102023000146260ustar 00000000000000//! # HTTP //! //! ```rust #![doc = include_str!("../../examples/http/parser.rs")] //! ``` winnow-0.6.26/src/_topic/ini.rs000064400000000000000000000001311046102023000144240ustar 00000000000000//! # INI //! //! ```rust #![doc = include_str!("../../examples/ini/parser.rs")] //! ``` winnow-0.6.26/src/_topic/json.rs000064400000000000000000000001531046102023000146220ustar 00000000000000//! # json //! //! ```rust,ignore #![doc = include_str!("../../examples/json/parser_dispatch.rs")] //! ``` winnow-0.6.26/src/_topic/language.rs000064400000000000000000000216151046102023000154420ustar 00000000000000//! # 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_till, //! }; //! //! pub fn peol_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> //! { //! ('%', take_till(1.., ['\n', '\r'])) //! .void() // Output is thrown away. //! .parse_next(i) //! } //! ``` //! //! ### `/* C-style comments */` //! //! Inline comments surrounded with sentinel literals `(*` and `*)`. This version returns an output of `()` //! and does not handle nested comments. //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! error::ParserError, //! token::take_until, //! }; //! //! pub fn pinline_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> { //! ( //! "(*", //! take_until(0.., "*)"), //! "*)" //! ) //! .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 == '_') //! ) //! .take() //! .parse_next(input) //! } //! ``` //! //! Let's say we apply this to the identifier `hello_world123abc`. The first element of the tuple //! would uses [`one_of`][crate::token::one_of] which would take `h`. The tuple ensures that //! `ello_world123abc` will be piped to the next [`take_while`][crate::token::take_while] parser, //! which takes every remaining character. However, the tuple returns a tuple of the results //! of its sub-parsers. The [`take`][crate::Parser::take] parser produces a `&str` of the //! input text that was parsed, which in this case is the entire `&str` `hello_world123abc`. //! //! ## Literal Values //! //! ### Escaped Strings //! //! ```rust #![doc = include_str!("../../examples/string/parser.rs")] //! ``` //! //! See also [`take_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, //! }; //! //! 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(|()| ()).take() //! ).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, //! }; //! //! fn hexadecimal_value(input: &mut &str) -> PResult { //! preceded( //! alt(("0x", "0X")), //! repeat(1.., //! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ())) //! ).map(|()| ()).take() //! ).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, //! }; //! //! 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(|()| ()).take() //! ).parse_next(input) //! } //! ``` //! //! #### Binary //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::alt, //! combinator::{repeat}, //! combinator::{preceded, terminated}, //! token::one_of, //! }; //! //! 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(|()| ()).take() //! ).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(|()| ()) //! .take() //! .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 //! )) //! ).take() //! , // Case two: 42e42 and 42.42e42 //! ( //! decimal, //! opt(preceded( //! '.', //! decimal, //! )), //! one_of(['e', 'E']), //! opt(one_of(['+', '-'])), //! decimal //! ).take() //! , // Case three: 42. and 42.42 //! ( //! decimal, //! '.', //! opt(decimal) //! ).take() //! )).parse_next(input) //! } //! //! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> { //! repeat(1.., //! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ())) //! ). //! map(|()| ()) //! .take() //! .parse_next(input) //! } //! ``` //! //! See also [`float`] #![allow(unused_imports)] use crate::ascii::dec_int; use crate::ascii::dec_uint; use crate::ascii::escaped_transform; use crate::ascii::float; use crate::ascii::hex_uint; use crate::ascii::take_escaped; winnow-0.6.26/src/_topic/mod.rs000064400000000000000000000017671046102023000144440ustar 00000000000000//! # Special Topics //! //! These are short recipes for accomplishing common tasks. //! //! - [Why `winnow`?][why] //! - [For `nom` users][nom] //! - 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] //! - [Debugging][crate::_tutorial::chapter_8] //! //! 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 nom; pub mod partial; pub mod performance; pub mod s_expression; pub mod stream; pub mod why; winnow-0.6.26/src/_topic/nom.rs000064400000000000000000000221341046102023000144450ustar 00000000000000//! # For `nom` users //! //! ## Migrating from `nom` //! //! For comparisons with `nom`, see //! - [Why `winnow`][super::why] //! - [parse-rosetta-rs](https://github.com/rosetta-rs/parse-rosetta-rs/) //! //! What approach you take depends on the size and complexity of your parser. //! For small, simple parsers, its likely easiest to directly port from `nom`. //! When trying to look for the equivalent of a `nom` combinator, search in the docs for the name //! of the `nom` combinator. It is expected that, where names diverge, a doc alias exists. //! See also the [List of combinators][crate::combinator]. //! //! ### Complex migrations //! //! For larger parsers, it is likely best to take smaller steps //! - Easier to debug when something goes wrong //! - Deprecation messages will help assist through the process //! //! The workflow goes something like: //! 1. Run `cargo rm nom && cargo add winnow@0.3` //! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [migration //! notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#nom-migration-guide)) //! 1. Commit //! 1. Switch any `impl FnMut(I) -> IResult` to `impl Parser` //! 1. Resolve deprecation messages //! 1. Commit //! 1. Run `cargo add winnow@0.4` //! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#040---2023-03-18) for more details) //! 1. Commit //! 1. Resolve deprecation messages //! 1. Commit //! 1. Run `cargo add winnow@0.5` //! 1. Ensure everything compiles and tests pass, ignoring deprecation messages (see [migration //! notes](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md#050---2023-07-13)) //! 1. Commit //! 1. Resolve deprecation messages //! 1. Commit //! //! ### Examples //! //! For example migrations, see //! - [git-config-env](https://github.com/gitext-rs/git-config-env/pull/11) (nom to winnow 0.3) //! - [git-conventional](https://github.com/crate-ci/git-conventional/pull/37) (nom to winnow 0.3, //! adds explicit tracing for easier debugging) //! - [typos](https://github.com/crate-ci/typos/pull/664) (nom to winnow 0.3) //! - [cargo-smart-release](https://github.com/Byron/gitoxide/pull/948) (gradual migration from nom //! to winnow 0.5) //! - [gix-config](https://github.com/Byron/gitoxide/pull/951) (gradual migration from nom //! to winnow 0.5) //! - [gix-protocol](https://github.com/Byron/gitoxide/pull/1009) (gradual migration from nom //! to winnow 0.5) //! - [gitoxide](https://github.com/Byron/gitoxide/pull/956) (gradual migration from nom //! to winnow 0.5) //! //! ## Differences //! //! These are key differences to help Nom users adapt to writing parsers with Winnow. //! //! ### Renamed APIs //! //! Names have changed for consistency or clarity. //! //! To find a parser you are looking for, //! - Search the docs for the `nom` parser //! - See the [List of combinators][crate::combinator] //! //! ### GATs //! //! `nom` v8 back-propagates how you will use a parser to parser functions using a language feature //! called GATs. //! Winnow has made the conscious choice not to use this feature, finding alternative ways of //! getting most of the benefits. //! //! Benefits of GATs: //! - Performance as the compiler is able to instantiate copies of a parser that are //! better tailored to how it will be used, like discarding unused allocations for output or //! errors. //! //! Benefits of not using GATs: //! - Predictable performance: //! With GATs, seemingly innocuous changes like choosing to hand write a parser using idiomatic function parsers //! (`fn(&mut I) -> Result`) can cause surprising slow downs because these functions sever the back-propagation from GATs. //! The causes of these slowdowns could be hard to identify by inspection or profiling. //! - No "eek out X% perf improvement" pressure to contort a parser //! that is more easily written imperatively //! to be written declaratively //! so it can preserve the back-propagation from GATs. //! - Built-in parsers serve are can serve as examples to users of idiomatic function parsers //! (`fn(&mut I) -> Result`). //! With GATs, built-in parsers tend to be complex implementations of traits. //! - Faster build times and smaller binary size as parsers only need to be generated for one mode, //! not upto 8 //! //! #### Partial/streaming parsers //! //! `nom` v8 back-propagates whether `Parser::parse_complete` was used to select `complete` //! parsers. //! Previously, users had ensure consistently using a parser from the `streaming` or `complete` module. //! //! Instead, you tag the input type (`I`) by wrapping it in [`Partial`] and parsers will adjust //! their behavior accordingly. //! See [partial] special topic. //! //! #### Eliding Output //! //! `nom` v8 back-propagates whether an Output will be used and skips its creation. //! For example, `value(Null, many0(_))` will avoid creating and pushing to a `Vec`. //! Previously, users had to select `count_many0` over `many0` to avoid creating a `Vec`. //! //! Instead, `repeat` returns an `impl Accumulate` which could be a `Vec`, a `usize` for `count` //! variants, or `()` to do no extra work. //! //! #### Eliding Backtracked Errors //! //! Under the hood, [`alt`] is an `if-not-error-else` ladder, see [`_tutorial::chapter_3`]. //! nom v8 back-propagates whether the error will be discarded and avoids any expensive work done //! for rich error messages. //! //! Instead, [`ContextError`] and other changes have made it so errors have very little overhead. //! [`dispatch!`] can also be used in some situations to avoid `alt`s overhead. //! //! ### Parsers return [`Stream::Slice`], rather than [`Stream`] //! //! In `nom`, parsers like [`take_while`] parse a [`Stream`] and return a [`Stream`]. //! When wrapping the input, like with [`Stateful`], //! you have to unwrap the input to integrate it in your application, //! and it requires [`Stream`] to be `Clone` //! (which requires `RefCell` for mutable external state and can be expensive). //! //! Instead, [`Stream::Slice`] was added to track the intended type for parsers to return. //! If you want to then parse the slice, you then need to take it and turn it back into a //! [`Stream`]. //! //! ### `&mut I` //! //! `winnow` switched from pure-function parser (`Fn(I) -> (I, O)` to `Fn(&mut I) -> O`). //! On error, `i` is left pointing at where the error happened. //! //! Benefits of `Fn(&mut I) -> O`: //! - Cleaner code: Removes need to pass `i` everywhere and makes changes to `i` more explicit //! - Correctness: No forgetting to chain `i` through a parser //! - Flexibility: `I` does not need to be `Copy` or even `Clone`. For example, [`Stateful`] can use `&mut S` instead of `RefCell`. //! - Performance: `Result::Ok` is smaller without `i`, reducing the risk that the output will be //! returned on the stack, rather than the much faster CPU registers. //! `Result::Err` can also be smaller because the error type does not need to carry `i` to point //! to the error. //! See also [#72](https://github.com/winnow-rs/winnow/issues/72). //! //! Benefits of `Fn(I) -> (I, O)`: //! - Pure functions can be easier to reason about //! - Less boilerplate in some situations (see below) //! - Less syntactic noise in some situations (see below) //! //! When returning a slice from the input, you have to add a lifetime: //! ```rust //! # use winnow::prelude::*; //! fn foo<'i>(i: &mut &'i str) -> ModalResult<&'i str> { //! # Ok("") //! // ... //! } //! ``` //! //! When writing a closure, you need to annotate the type //! ```rust //! # use winnow::prelude::*; //! # use winnow::combinator::alt; //! # use winnow::error::ContextError; //! # let mut input = ""; //! # fn foo<'i>() -> impl ModalParser<&'i str, &'i str, ContextError> { //! alt(( //! |i: &mut _| { //! # Ok("") //! // ... //! }, //! |i: &mut _| { //! # Ok("") //! // ... //! }, //! )) //! # } //! ``` //! *(at least the full type isn't needed)* //! //! To save and restore from intermediate states, [`Stream::checkpoint`] and [`Stream::reset`] can help: //! ```rust //! use winnow::stream::Stream as _; //! # let mut i = ""; //! # let i = &mut i; //! //! let start = i.checkpoint(); //! // ... //! i.reset(&start); //! ``` //! //! When the Output of a parser is a slice, you have to add a lifetime: //! ```rust //! # use winnow::prelude::*; //! fn foo<'i>(i: &mut &'i str) -> PResult<&'i str> { //! // ... //! # winnow::token::rest.parse_next(i) //! } //! ``` //! //! When writing a closure, you need to annotate the type: //! ```rust //! # use winnow::prelude::*; //! # use winnow::combinator::trace; //! fn foo(i: &mut &str) -> PResult { //! trace("foo", |i: &mut _| { //! // ... //! # Ok(0) //! }).parse_next(i) //! } //! ``` #![allow(unused_imports)] use crate::_topic::partial; use crate::_tutorial; use crate::combinator::alt; use crate::combinator::dispatch; use crate::error::ContextError; use crate::error::ErrMode; use crate::error::ModalResult; use crate::stream::Accumulate; use crate::stream::Partial; use crate::stream::Stateful; use crate::stream::Stream; use crate::token::take_while; winnow-0.6.26/src/_topic/partial.rs000064400000000000000000000035411046102023000153110ustar 00000000000000//! # 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_and_then`] //! - [`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_until(0.., 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_and_then; use crate::combinator::repeat; use crate::error::ErrMode::Incomplete; use crate::error::Needed; use crate::stream::Partial; use crate::stream::StreamIsPartial; winnow-0.6.26/src/_topic/performance.rs000064400000000000000000000035261046102023000161610ustar 00000000000000//! # 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() -> impl Parser //! # where //! # I: winnow::stream::Stream, //! # I: winnow::stream::StreamIsPartial, //! # E: winnow::error::ParserError, //! { //! // ...some chained combinators... //! # winnow::token::any //! } //! ``` //! //! Instead, wrap the combinators in a closure to simplify the type: //! ```rust //! # use winnow::prelude::*; //! fn foo() -> impl Parser //! # where //! # I: winnow::stream::Stream, //! # I: winnow::stream::StreamIsPartial, //! # E: winnow::error::ParserError, //! { //! 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; winnow-0.6.26/src/_topic/s_expression.rs000064400000000000000000000001531046102023000163720ustar 00000000000000//! # s-expression //! //! ```rust #![doc = include_str!("../../examples/s_expression/parser.rs")] //! ``` winnow-0.6.26/src/_topic/stream.rs000064400000000000000000000043521046102023000151510ustar 00000000000000//! # 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 //! - [`LocatingSlice`] for looking up the absolute position of a token //! //! But that won't always cut it for your parser. For example, you might lex `&str` into //! a series of tokens and then want to parse a `TokenStream`. //! //! ## 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`. //! ```rust //! # use winnow::prelude::*; //! # type MyStream<'i> = &'i str; //! # type Output<'i> = &'i str; //! fn parser<'s>(i: &mut MyStream<'s>) -> PResult> { //! "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::*; winnow-0.6.26/src/_topic/why.rs000064400000000000000000000125471046102023000144720ustar 00000000000000//! # 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][trace] for easier debugging //! - [Streaming parsing][Partial] for network communication or large file //! - [Stateful] parsers //! //! For binary formats, `winnow` includes: //! - [A hexadecimal view][crate::Bytes] in [trace] //! - [TLV](https://en.wikipedia.org/wiki/Type-length-value) (e.g. [`length_take`]) //! - Some common parsers to help get started, like numbers //! //! For text formats, `winnow` includes: //! - [Tracking of spans][crate::LocatingSlice] //! - [A textual view when parsing as bytes][crate::BStr] in [trace] //! - Ability to evaluate directly, parse to an AST, or lex and parse the format //! //! This works well for: //! - 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. //! //! For more details, see the [design differences][super::nom#api-differences]. //! //! See also our [nom migration guide][super::nom#migrating-from-nom]. //! //! ## `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 `chumsky` 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::binary::length_take; use crate::combinator::trace; use crate::stream::Accumulate; use crate::stream::Partial; use crate::stream::Stateful; winnow-0.6.26/src/_tutorial/chapter_0.rs000064400000000000000000000026611046102023000162510ustar 00000000000000//! # 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: //! //! - Individual parser functions are small, focused on one thing, ignoring the rest //! - You can write tests focused on individual parsers (unit tests and property-based tests) //! in addition to testing the top-level parser as a whole. //! - Top-level parsing code looks close to the grammar you would have written #![allow(unused_imports)] use crate::_topic; use std::iter::Iterator; pub use super::chapter_1 as next; pub use crate::_tutorial as table_of_contents; winnow-0.6.26/src/_tutorial/chapter_1.rs000064400000000000000000000065061046102023000162540ustar 00000000000000//! # 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 compose more complex parsers from more simple //! ones (using "combinators"). //! //! Let's discuss what a "parser" actually does. A parser takes an input and advances it until it returns //! a result, where: //! - `Ok` indicates the parser successfully found what it was looking for; or //! - `Err` indicates the parser could not find what it was looking for. //! //! Parsers do more than just return a binary "success"/"failure" code. //! - On success, the parser will return the processed data. The input will be advanced to the end of //! what was processed, pointing to what will be parsed next. //! - If the parser failed, then there are multiple errors that could be returned. //! We'll explore this further in [`chapter_7`]. //! //! ```text //! ┌─► Ok(what matched the parser) //! ┌────────┐ │ //! my input───►│a parser├──►either──┤ //! └────────┘ └─► Err(...) //! ``` //! //! //! To represent this model of the world, winnow uses the [`PResult`] 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`] //! trait comes in with [`Parser::parse_next`] being the primary way to drive //! parsing forward. //! In [`chapter_6`], we'll cover how to integrate these into your application, particularly with //! [`Parser::parse`]. //! //! You'll note that `I` and `O` are parameterized -- while most of the examples in this book //! will be with `&str` (i.e. parsing a string); they do not have to be strings; nor do they //! have to be the same type (consider the simple example where `I = &str`, and `O = u64` -- this //! parses a string into an unsigned integer.) //! //! # Let's write our first parser! //! //! The simplest parser we can write is one which successfully does nothing. //! //! To make it easier to implement a [`Parser`], the trait is implemented for //! functions of the form `Fn(&mut I) -> PResult`. //! //! 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 input will be left where it started. //! - 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 super::chapter_6; use super::chapter_7; 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; winnow-0.6.26/src/_tutorial/chapter_2.rs000064400000000000000000000222271046102023000162530ustar 00000000000000//! # 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 { //! 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()); //! } //! ``` //! //! This extraction of a token is encapsulated in the [`any`] parser: //! ```rust //! # use winnow::PResult; //! # use winnow::error::ParserError; //! # use winnow::error::ErrorKind; //! # use winnow::error::ErrMode; //! use winnow::Parser; //! use winnow::token::any; //! //! fn parse_prefix(input: &mut &str) -> PResult { //! let c = any //! .parse_next(input)?; //! if c != '0' { //! return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); //! } //! Ok(c) //! } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let output = parse_prefix.parse_next(&mut input).unwrap(); //! # //! # assert_eq!(input, "x1a2b Hello"); //! # assert_eq!(output, '0'); //! # //! # assert!(parse_prefix.parse_next(&mut "d").is_err()); //! # } //! ``` //! //! Using the higher level [`any`] parser opens `parse_prefix` to the helpers on the [`Parser`] trait, //! like [`Parser::verify`] which fails a parse if a condition isn't met, like our check above: //! ```rust //! # use winnow::PResult; //! use winnow::Parser; //! use winnow::token::any; //! //! fn parse_prefix(input: &mut &str) -> PResult { //! let c = any //! .verify(|c| *c == '0') //! .parse_next(input)?; //! 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()); //! # } //! ``` //! //! Matching a single token literal is common enough that [`Parser`] is implemented for //! the `char` type, encapsulating both [`any`] and [`Parser::verify`]: //! ```rust //! # use winnow::PResult; //! use winnow::Parser; //! //! fn parse_prefix(input: &mut &str) -> PResult { //! let c = '0'.parse_next(input)?; //! 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()); //! # } //! ``` //! //! ## 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()); //! } //! ``` //! //! Matching the input position against a string literal is encapsulated in the [`literal`] parser: //! ```rust //! # use winnow::PResult; //! # use winnow::Parser; //! use winnow::token::literal; //! //! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! let expected = "0x"; //! let actual = literal(expected).parse_next(input)?; //! Ok(actual) //! } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let output = parse_prefix.parse_next(&mut input).unwrap(); //! # assert_eq!(input, "1a2b Hello"); //! # assert_eq!(output, "0x"); //! # //! # assert!(parse_prefix.parse_next(&mut "0o123").is_err()); //! # } //! ``` //! //! Like for a single token, matching a string literal is common enough that [`Parser`] is implemented for the `&str` type: //! ```rust //! # use winnow::PResult; //! use winnow::Parser; //! //! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! let actual = "0x".parse_next(input)?; //! Ok(actual) //! } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let output = parse_prefix.parse_next(&mut input).unwrap(); //! # assert_eq!(input, "1a2b Hello"); //! # assert_eq!(output, "0x"); //! # //! # assert!(parse_prefix.parse_next(&mut "0o123").is_err()); //! # } //! ``` //! //! See [`token`] for additional individual and token-slice parsers. //! //! ## Character Classes //! //! Selecting a single `char` or a [`literal`] is fairly limited. Sometimes, you will want to select one of several //! `chars` of a specific class, like digits. For this, we use the [`one_of`] parser: //! //! ```rust //! # use winnow::Parser; //! # use winnow::PResult; //! use winnow::token::one_of; //! //! fn parse_digits(input: &mut &str) -> PResult { //! 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::literal; use crate::token::one_of; 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; winnow-0.6.26/src/_tutorial/chapter_3.rs000064400000000000000000000255521046102023000162600ustar 00000000000000//! # Chapter 3: Sequencing and Alternatives //! //! In the last chapter, we saw how to create parsers using prebuilt parsers. //! //! In this chapter, we explore two other widely used features: //! sequencing and alternatives. //! //! ## 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 literal 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. //! //! To retry a parse result, we can save off a [`Stream::checkpoint`] and later restore the parser //! back to that position with [`Stream::reset`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let 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_7`#error-cuts] //! //!
//! //! [`opt`] is a parser that encapsulates this pattern of "retry on failure": //! ```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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let 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:** [`empty`] 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let 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_7; use crate::combinator; use crate::combinator::alt; use crate::combinator::dispatch; use crate::combinator::empty; use crate::combinator::fail; use crate::combinator::opt; use crate::combinator::peek; use crate::combinator::preceded; 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; winnow-0.6.26/src/_tutorial/chapter_4.rs000064400000000000000000000066241046102023000162600ustar 00000000000000//! # 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`. //! //! 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 { //! 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 { //! 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let 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; winnow-0.6.26/src/_tutorial/chapter_5.rs000064400000000000000000000232431046102023000162550ustar 00000000000000//! # 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> { //! 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 { //! # 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let 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> { //! repeat(0.., //! terminated(parse_digits, opt(',')) //! ).parse_next(input) //! } //! # //! # fn parse_digits(input: &mut &str) -> PResult { //! # 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let 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 `,`. However, if that's not desired, it //! can easily be fixed by using [`separated`] instead of [`repeat`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::dispatch; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! use winnow::combinator::separated; //! //! fn parse_list(input: &mut &str) -> PResult> { //! separated(0.., parse_digits, ",").parse_next(input) //! } //! //! // ... //! # fn parse_digits(input: &mut &str) -> PResult { //! # 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let 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 [`separated`] and [`repeat`], they aren't limited to collecting //! the result into a [`Vec`], but rather anything that implements the [`Accumulate`] trait. //! [`Accumulate`] is for instance also implemented for [`HashSet`], [`String`] and `()`. //! //! This lets us build more complex parsers than we did in //! [`chapter_2`] by accumulating the results into a `()` and [`take`][Parser::take]-ing //! the consumed input. //! //! `take` works by //! 1. Creating a [`checkpoint`][Stream::checkpoint] //! 2. Running the inner parser, in our case the `parse_list` parser, which will advance the input //! 3. Returning the slice from the first checkpoint to the current position. //! //! Since the result of `parse_list` gets thrown away, we //! accumulates into a `()` to not waste work creating an unused `Vec`. //! //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::dispatch; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::combinator::separated; //! # //! fn take_list<'s>(input: &mut &'s str) -> PResult<&'s str> { //! parse_list.take().parse_next(input) //! } //! //! fn parse_list(input: &mut &str) -> PResult<()> { //! separated(0.., parse_digits, ",").parse_next(input) //! } //! //! # fn parse_digits(input: &mut &str) -> PResult { //! # 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello"; //! //! let digits = take_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::separated; use crate::stream::Accumulate; use crate::stream::Stream; use crate::Parser; use std::collections::HashSet; use std::vec::Vec; pub use super::chapter_4 as previous; pub use super::chapter_6 as next; pub use crate::_tutorial as table_of_contents; winnow-0.6.26/src/_tutorial/chapter_6.rs000064400000000000000000000100061046102023000162470ustar 00000000000000//! # Chapter 6: 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; //! use winnow::PResult; //! //! pub fn parser<'s>(input: &mut &'s str) -> PResult<&'s str> { //! // ... //! # Ok("") //! } //! ``` //! 1. We have to decide what to do about the "remainder" of the `input`. //! 2. The [`PResult`] is not compatible with the rest of the Rust ecosystem. //! Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static` //! meaning: //! - They implement the [`std::error::Error`] trait //! - They can be sent across threads //! - They are safe to be referenced across threads //! - They do not borrow //! //! winnow provides [`Parser::parse`] to help with this: //! - Ensures we hit [`eof`] //! - Converts from [`PResult`] to [`Result`] //! - Wraps the error in [`ParseError`] //! - For simple cases, [`ParseError`] provides a [`std::fmt::Display`] impl to render the error. //! - For more involved cases, [`ParseError`] provides the original [`input`][ParseError::input] and the //! [`offset`][ParseError::offset] of where it failed so you can capture this information in //! your error, [rendering it as you wish][chapter_7#error-adaptation-and-rendering]. //! //! However, [`ParseError`] will still need some level of adaptation to integrate with your //! application's error type (like with `?`). //! //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::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 = anyhow::Error; //! //! fn from_str(input: &str) -> Result { //! parse_digits //! .map(Hex) //! .parse(input) //! .map_err(|e| anyhow::format_err!("{e}")) //! } //! } //! //! // ... //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult { //! # 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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let input = "0x1a2b"; //! assert_eq!(input.parse::().unwrap(), Hex(0x1a2b)); //! //! let input = "0x1a2b Hello"; //! assert!(input.parse::().is_err()); //! let input = "ghiHello"; //! assert!(input.parse::().is_err()); //! } //! ``` #![allow(unused_imports)] use super::chapter_1; use super::chapter_7; 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_5 as previous; pub use super::chapter_7 as next; pub use crate::_tutorial as table_of_contents; winnow-0.6.26/src/_tutorial/chapter_7.rs000064400000000000000000000620061046102023000162570ustar 00000000000000//! # Chapter 7: Error Reporting //! //! ## Context //! //! With [`Parser::parse`] we get errors that point to the failure but don't explain the reason for //! the failure: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! # //! # #[derive(Debug, PartialEq, Eq)] //! # pub struct Hex(usize); //! # //! # impl std::str::FromStr for Hex { //! # type Err = String; //! # //! # fn from_str(input: &str) -> Result { //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! # .parse(input) //! # .map_err(|e| e.to_string()) //! # } //! # } //! # //! // ... //! //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! # alt(( //! # ("0b", parse_bin_digits), //! # ("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'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "0xZZ"; //! let error = "\ //! 0xZZ //! ^ //! "; //! assert_eq!(input.parse::().unwrap_err(), error); //! } //! ``` //! //! Back in [`chapter_1`], we glossed over the `Err` variant of [`PResult`]. `PResult` is //! actually short for `PResult` where [`ContextError`] is a relatively cheap //! way of building up reasonable errors for humans. //! //! You can use [`Parser::context`] to annotate the error with custom types //! while unwinding to further clarify the error: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! use winnow::error::StrContext; //! use winnow::error::StrContextValue; //! //! # //! # #[derive(Debug, PartialEq, Eq)] //! # pub struct Hex(usize); //! # //! # impl std::str::FromStr for Hex { //! # type Err = String; //! # //! # fn from_str(input: &str) -> Result { //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! # .parse(input) //! # .map_err(|e| e.to_string()) //! # } //! # } //! # //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! alt(( //! ("0b", parse_bin_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("binary"))), //! ("0o", parse_oct_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("octal"))), //! ("0d", parse_dec_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("decimal"))), //! ("0x", parse_hex_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))), //! )).parse_next(input) //! } //! //! // ... //! //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "0xZZ"; //! let error = "\ //! 0xZZ //! ^ //! invalid digit //! expected hexadecimal"; //! assert_eq!(input.parse::().unwrap_err(), error); //! } //! ``` //! //! If you remember back to [`chapter_3`], [`alt`] will only report the last error. //! So if the parsers fail for any reason, like a bad radix, it will be reported as an invalid //! hexadecimal value: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! # use winnow::error::StrContext; //! # use winnow::error::StrContextValue; //! # //! # //! # #[derive(Debug, PartialEq, Eq)] //! # pub struct Hex(usize); //! # //! # impl std::str::FromStr for Hex { //! # type Err = String; //! # //! # fn from_str(input: &str) -> Result { //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! # .parse(input) //! # .map_err(|e| e.to_string()) //! # } //! # } //! # //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! # alt(( //! # ("0b", parse_bin_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("binary"))), //! # ("0o", parse_oct_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("octal"))), //! # ("0d", parse_dec_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("decimal"))), //! # ("0x", parse_hex_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))), //! # )).parse_next(input) //! # } //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "100"; //! let error = "\ //! 100 //! ^ //! invalid digit //! expected hexadecimal"; //! assert_eq!(input.parse::().unwrap_err(), error); //! } //! ``` //! We can improve this with [`fail`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! use winnow::error::StrContext; //! use winnow::error::StrContextValue; //! //! # //! # #[derive(Debug, PartialEq, Eq)] //! # pub struct Hex(usize); //! # //! # impl std::str::FromStr for Hex { //! # type Err = String; //! # //! # fn from_str(input: &str) -> Result { //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! # .parse(input) //! # .map_err(|e| e.to_string()) //! # } //! # } //! # //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! alt(( //! ("0b", parse_bin_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("binary"))), //! ("0o", parse_oct_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("octal"))), //! ("0d", parse_dec_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("decimal"))), //! ("0x", parse_hex_digits) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))), //! fail //! .context(StrContext::Label("radix prefix")) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0b"))) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0o"))) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0d"))) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))), //! )).parse_next(input) //! } //! //! // ... //! //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "100"; //! let error = "\ //! 100 //! ^ //! invalid radix prefix //! expected `0b`, `0o`, `0d`, `0x`"; //! assert_eq!(input.parse::().unwrap_err(), error); //! } //! ``` //! //! ## Error Cuts //! //! We still have the issue that we are falling-through when the radix is valid but the digits //! don't match it: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! # use winnow::error::StrContext; //! # use winnow::error::StrContextValue; //! # //! # //! # #[derive(Debug, PartialEq, Eq)] //! # pub struct Hex(usize); //! # //! # impl std::str::FromStr for Hex { //! # type Err = String; //! # //! # fn from_str(input: &str) -> Result { //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! # .parse(input) //! # .map_err(|e| e.to_string()) //! # } //! # } //! # //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! # alt(( //! # ("0b", parse_bin_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("binary"))), //! # ("0o", parse_oct_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("octal"))), //! # ("0d", parse_dec_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("decimal"))), //! # ("0x", parse_hex_digits) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))), //! # fail //! # .context(StrContext::Label("radix prefix")) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0b"))) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0o"))) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0d"))) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))), //! # )).parse_next(input) //! # } //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "0b5"; //! let error = "\ //! 0b5 //! ^ //! invalid radix prefix //! expected `0b`, `0o`, `0d`, `0x`"; //! assert_eq!(input.parse::().unwrap_err(), error); //! } //! ``` //! //! Let's break down `PResult` one step further: //! ```rust //! # use winnow::error::ErrorKind; //! # use winnow::error::ErrMode; //! pub type PResult = Result>; //! ``` //! [`PResult`] is just a fancy wrapper around `Result` that wraps our error in an [`ErrMode`] //! type. //! //! [`ErrMode`] is an enum with [`Backtrack`] and [`Cut`] variants (ignore [`Incomplete`] as its only //! relevant for [streaming][_topic::stream]). By default, errors are [`Backtrack`], meaning that //! other parsing branches will be attempted on failure, like the next case of an [`alt`]. [`Cut`] //! shortcircuits all other branches, immediately reporting the error. //! //! So we can get the correct `context` by modifying the above example with [`cut_err`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! # use winnow::error::StrContext; //! # use winnow::error::StrContextValue; //! use winnow::combinator::cut_err; //! //! # //! # #[derive(Debug, PartialEq, Eq)] //! # pub struct Hex(usize); //! # //! # impl std::str::FromStr for Hex { //! # type Err = String; //! # //! # fn from_str(input: &str) -> Result { //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! # .parse(input) //! # .map_err(|e| e.to_string()) //! # } //! # } //! # //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! alt(( //! ("0b", cut_err(parse_bin_digits)) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("binary"))), //! ("0o", cut_err(parse_oct_digits)) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("octal"))), //! ("0d", cut_err(parse_dec_digits)) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("decimal"))), //! ("0x", cut_err(parse_hex_digits)) //! .context(StrContext::Label("digit")) //! .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))), //! fail //! .context(StrContext::Label("radix prefix")) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0b"))) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0o"))) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0d"))) //! .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))), //! )).parse_next(input) //! } //! //! // ... //! //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "0b5"; //! let error = "\ //! 0b5 //! ^ //! invalid digit //! expected binary"; //! assert_eq!(input.parse::().unwrap_err(), error); //! } //! ``` //! //! ## Error Adaptation and Rendering //! //! While Winnow can provide basic rendering of errors, your application can have various demands //! beyond the basics provided like //! - Correctly reporting columns with unicode //! - Conforming to a specific layout //! //! For example, to get rustc-like errors with [`annotate-snippets`](https://crates.io/crates/annotate-snippets): //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::Parser; //! # use winnow::error::ParseError; //! # use winnow::error::ContextError; //! # use winnow::error::StrContext; //! # use winnow::error::StrContextValue; //! # use winnow::combinator::cut_err; //! # //! # //! #[derive(Debug, PartialEq, Eq)] //! pub struct Hex(usize); //! //! impl std::str::FromStr for Hex { //! type Err = HexError; //! //! fn from_str(input: &str) -> Result { //! // ... //! # parse_digits //! # .try_map(|(t, v)| match t { //! # "0b" => usize::from_str_radix(v, 2), //! # "0o" => usize::from_str_radix(v, 8), //! # "0d" => usize::from_str_radix(v, 10), //! # "0x" => usize::from_str_radix(v, 16), //! # _ => unreachable!("`parse_digits` doesn't return `{t}`"), //! # }) //! # .map(Hex) //! .parse(input) //! .map_err(|e| HexError::from_parse(e, input)) //! } //! } //! //! #[derive(Debug)] //! pub struct HexError { //! message: String, //! // Byte spans are tracked, rather than line and column. //! // This makes it easier to operate on programmatically //! // and doesn't limit us to one definition for column count //! // which can depend on the output medium and application. //! span: std::ops::Range, //! input: String, //! } //! //! impl HexError { //! fn from_parse(error: ParseError<&str, ContextError>, input: &str) -> Self { //! // The default renderer for `ContextError` is still used but that can be //! // customized as well to better fit your needs. //! let message = error.inner().to_string(); //! let input = input.to_owned(); //! let start = error.offset(); //! // Assume the error span is only for the first `char`. //! // Semantic errors are free to choose the entire span returned by `Parser::with_span`. //! let end = (start + 1..).find(|e| input.is_char_boundary(*e)).unwrap_or(start); //! Self { //! message, //! span: start..end, //! input, //! } //! } //! } //! //! impl std::fmt::Display for HexError { //! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { //! let message = annotate_snippets::Level::Error.title(&self.message) //! .snippet(annotate_snippets::Snippet::source(&self.input) //! .fold(true) //! .annotation(annotate_snippets::Level::Error.span(self.span.clone())) //! ); //! let renderer = annotate_snippets::Renderer::plain(); //! let rendered = renderer.render(message); //! rendered.fmt(f) //! } //! } //! //! impl std::error::Error for HexError {} //! //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! # alt(( //! # ("0b", cut_err(parse_bin_digits)) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("binary"))), //! # ("0o", cut_err(parse_oct_digits)) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("octal"))), //! # ("0d", cut_err(parse_dec_digits)) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("decimal"))), //! # ("0x", cut_err(parse_hex_digits)) //! # .context(StrContext::Label("digit")) //! # .context(StrContext::Expected(StrContextValue::Description("hexadecimal"))), //! # fail //! # .context(StrContext::Label("radix prefix")) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0b"))) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0o"))) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0d"))) //! # .context(StrContext::Expected(StrContextValue::StringLiteral("0x"))), //! # )).parse_next(input) //! # } //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='1'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! fn main() { //! let input = "0b5"; //! let error = "\ //! error: invalid digit //! expected binary //! | //! 1 | 0b5 //! | ^ //! |"; //! assert_eq!(input.parse::().unwrap_err().to_string(), error); //! } //! ``` #![allow(unused_imports)] use super::chapter_1; use super::chapter_3; use crate::combinator::alt; use crate::combinator::cut_err; use crate::combinator::fail; use crate::error::ContextError; use crate::error::ErrMode; use crate::error::ErrMode::*; use crate::error::ErrorKind; use crate::PResult; use crate::Parser; use crate::_topic; pub use super::chapter_6 as previous; pub use super::chapter_8 as next; pub use crate::_tutorial as table_of_contents; winnow-0.6.26/src/_tutorial/chapter_8.rs000064400000000000000000000022501046102023000162530ustar 00000000000000//! # Chapter 8: Debugging //! //! When things inevitably go wrong, you can introspect the parsing state by running your test case //! with `--features winnow/debug`: //! //! ![Trace output from string example](https://raw.githubusercontent.com/winnow-rs/winnow/main/assets/trace.svg "Example output") //! //! You can extend your own parsers to show up by wrapping their body with //! [`trace`][crate::combinator::trace]. Going back to [`do_nothing_parser`][super::chapter_1]. //! ```rust //! # use winnow::PResult; //! # use winnow::Parser; //! use winnow::combinator::trace; //! //! pub fn do_nothing_parser<'s>(input: &mut &'s str) -> PResult<&'s str> { //! trace( //! "do_nothing_parser", //! |i: &mut _| Ok("") //! ).parse_next(input) //! } //! # //! # 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, ""); //! # } //! ``` pub use super::chapter_7 as previous; pub use crate::_tutorial as table_of_contents; winnow-0.6.26/src/_tutorial/mod.rs000064400000000000000000000003741046102023000151620ustar 00000000000000//! # 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; pub mod chapter_8; winnow-0.6.26/src/ascii/mod.rs000064400000000000000000002040711046102023000142500ustar 00000000000000//! 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::dispatch; use crate::combinator::empty; use crate::combinator::fail; use crate::combinator::opt; use crate::combinator::trace; use crate::error::ParserError; use crate::error::{ErrMode, ErrorKind, Needed}; use crate::stream::FindSlice; use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial}; use crate::stream::{Compare, CompareResult}; use crate::token::any; use crate::token::one_of; use crate::token::take_until; use crate::token::take_while; use crate::PResult; use crate::Parser; /// Mark a value as case-insensitive for ASCII characters /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; /// # use winnow::ascii::Caseless; /// /// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// Caseless("hello").parse_next(s) /// } /// /// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello"))); /// assert_eq!(parser.parse_peek("hello, World!"), Ok((", World!", "hello"))); /// assert_eq!(parser.parse_peek("HeLlo, World!"), Ok((", World!", "HeLlo"))); /// assert_eq!(parser.parse_peek("Some"), Err(ErrMode::Backtrack(InputError::new("Some", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` #[derive(Copy, Clone, Debug)] pub struct Caseless(pub T); impl Caseless<&str> { /// Get the byte-representation of this case-insensitive value #[inline(always)] pub fn as_bytes(&self) -> Caseless<&[u8]> { Caseless(self.0.as_bytes()) } } /// Recognizes the string `"\r\n"`. /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn crlf<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::crlf.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream + Compare<&'static str>, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn till_line_ending<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::till_line_ending.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::till_line_ending; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// till_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::till_line_ending; /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Ok((Partial::new("\r\nc"), "ab"))); /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::Unknown))); /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::Unknown))); /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rb\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rb\nc"), ErrorKind::Tag )))); /// assert_eq!(till_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rbc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rbc"), ErrorKind::Tag )))); /// ``` #[inline(always)] pub fn till_line_ending(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream + Compare<&'static str> + FindSlice<(char, char)>, ::Token: AsChar + Clone, Error: ParserError, { trace("till_line_ending", move |input: &mut Input| { if ::is_partial_supported() { till_line_ending_::<_, _, true>(input) } else { till_line_ending_::<_, _, false>(input) } }) .parse_next(input) } fn till_line_ending_, const PARTIAL: bool>( input: &mut I, ) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, I: FindSlice<(char, char)>, ::Token: AsChar + Clone, { let res = match take_until(0.., ('\r', '\n')).parse_next(input) { Ok(slice) => slice, Err(ErrMode::Backtrack(_)) => input.finish(), Err(err) => { return Err(err); } }; if matches!(input.compare("\r"), CompareResult::Ok(_)) { let comp = input.compare("\r\n"); match comp { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn line_ending<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::line_ending.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream + Compare<&'static str>, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn newline(input: &mut &str) -> PResult /// # { /// # winnow::ascii::newline.parse_next(input) /// # } /// ``` /// /// # 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> { /// 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::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::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::Tag)))); /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn newline>(input: &mut I) -> PResult where I: StreamIsPartial, I: Stream, I: Compare, { trace("newline", '\n').parse_next(input) } /// Matches a tab character `'\t'`. /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn tab(input: &mut &str) -> PResult /// # { /// # winnow::ascii::tab.parse_next(input) /// # } /// ``` /// /// # 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> { /// 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::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::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::Tag)))); /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn tab(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream + Compare, Error: ParserError, { trace("tab", '\t').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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphabetic character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn alpha0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::alpha0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphabetic character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn alpha1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::alpha1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non digit character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn digit0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::digit0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non digit character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn digit1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::digit1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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> { /// 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non hexadecimal digit character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn hex_digit0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::hex_digit0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non hexadecimal digit character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn hex_digit1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::hex_digit1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non octal digit character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn oct_digit0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::oct_digit0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial, Input: Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non octal digit character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn oct_digit1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::oct_digit1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphanumerical character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn alphanumeric0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::alphanumeric0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphanumerical character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn alphanumeric1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::alphanumeric1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn space0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::space0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { trace("space0", take_while(0.., AsChar::is_space)).parse_next(input) } /// Recognizes one or more spaces and tabs. /// /// *Complete version*: Will return the whole input if no terminating token is found (a non space /// character). /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn space1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::space1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn multispace0<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::multispace0.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar + Clone, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn multispace1<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::ascii::multispace1.parse_next(input) /// # } /// ``` /// /// # Example /// /// ``` /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: StreamIsPartial + Stream, ::Token: AsChar + Clone, Error: ParserError, { 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] into a `u32`: /// ```rust /// # use winnow::prelude::*;; /// pub fn dec_uint(input: &mut &str) -> PResult /// # { /// # winnow::ascii::dec_uint.parse_next(input) /// # } /// ``` #[doc(alias = "u8")] #[doc(alias = "u16")] #[doc(alias = "u32")] #[doc(alias = "u64")] #[doc(alias = "u128")] pub fn dec_uint(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, ::Slice: AsBStr, ::Token: AsChar + Clone, Output: Uint, Error: ParserError, { trace("dec_uint", move |input: &mut Input| { alt(((one_of('1'..='9'), digit0).void(), one_of('0').void())) .take() .verify_map(|s: ::Slice| { let s = s.as_bstr(); // SAFETY: Only 7-bit ASCII characters are parsed let s = unsafe { crate::lib::std::str::from_utf8_unchecked(s) }; Output::try_from_dec_uint(s) }) .parse_next(input) }) .parse_next(input) } /// Metadata for parsing unsigned integers, see [`dec_uint`] pub trait Uint: Sized { #[doc(hidden)] fn try_from_dec_uint(slice: &str) -> Option; } impl Uint for u8 { fn try_from_dec_uint(slice: &str) -> Option { slice.parse().ok() } } impl Uint for u16 { fn try_from_dec_uint(slice: &str) -> Option { slice.parse().ok() } } impl Uint for u32 { fn try_from_dec_uint(slice: &str) -> Option { slice.parse().ok() } } impl Uint for u64 { fn try_from_dec_uint(slice: &str) -> Option { slice.parse().ok() } } impl Uint for u128 { fn try_from_dec_uint(slice: &str) -> Option { slice.parse().ok() } } impl Uint for usize { fn try_from_dec_uint(slice: &str) -> Option { slice.parse().ok() } } /// Decode a decimal signed integer (e.g. [`i32`]) /// /// *Complete version*: can parse until the end of input. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] into an `i32`: /// ```rust /// # use winnow::prelude::*;; /// pub fn dec_int(input: &mut &str) -> PResult /// # { /// # winnow::ascii::dec_int.parse_next(input) /// # } /// ``` #[doc(alias = "i8")] #[doc(alias = "i16")] #[doc(alias = "i32")] #[doc(alias = "i64")] #[doc(alias = "i128")] pub fn dec_int(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, ::Slice: AsBStr, ::Token: AsChar + Clone, Output: Int, Error: ParserError, { trace("dec_int", move |input: &mut Input| { let sign = opt(dispatch! {any.map(AsChar::as_char); '+' => empty.value(true), '-' => empty.value(false), _ => fail, }); alt(((sign, one_of('1'..='9'), digit0).void(), one_of('0').void())) .take() .verify_map(|s: ::Slice| { let s = s.as_bstr(); // SAFETY: Only 7-bit ASCII characters are parsed let s = unsafe { crate::lib::std::str::from_utf8_unchecked(s) }; Output::try_from_dec_int(s) }) .parse_next(input) }) .parse_next(input) } /// Metadata for parsing signed integers, see [`dec_int`] pub trait Int: Sized { #[doc(hidden)] fn try_from_dec_int(slice: &str) -> Option; } impl Int for i8 { fn try_from_dec_int(slice: &str) -> Option { slice.parse().ok() } } impl Int for i16 { fn try_from_dec_int(slice: &str) -> Option { slice.parse().ok() } } impl Int for i32 { fn try_from_dec_int(slice: &str) -> Option { slice.parse().ok() } } impl Int for i64 { fn try_from_dec_int(slice: &str) -> Option { slice.parse().ok() } } impl Int for i128 { fn try_from_dec_int(slice: &str) -> Option { slice.parse().ok() } } impl Int for isize { fn try_from_dec_int(slice: &str) -> Option { slice.parse().ok() } } /// 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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input /// is hit before a hard boundary (non-hex character, more characters than supported). /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] into a `u32`: /// ```rust /// # use winnow::prelude::*;; /// pub fn hex_uint(input: &mut &str) -> PResult /// # { /// # winnow::ascii::hex_uint.parse_next(input) /// # } /// ``` /// /// # 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> { /// 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>> { /// 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, ::Token: AsChar, ::Slice: AsBStr, Output: HexUint, Error: ParserError, { trace("hex_uint", move |input: &mut Input| { let invalid_offset = input .offset_for(|c| { let c = c.as_char(); !"0123456789abcdefABCDEF".contains(c) }) .unwrap_or_else(|| input.eof_offset()); let max_nibbles = Output::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 ::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 = Output::default(); for c in parsed.as_bstr() { let nibble = *c as char; let nibble = nibble.to_digit(16).unwrap_or(0) as u8; let nibble = Output::from(nibble); res = (res << Output::from(4)) + nibble; } Ok(res) }) .parse_next(input) } /// Metadata for parsing hex numbers, see [`hex_uint`] pub trait HexUint: Default + Shl + Add + From { #[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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] into an `f64`: /// ```rust /// # use winnow::prelude::*;; /// pub fn float(input: &mut &str) -> PResult /// # { /// # winnow::ascii::float.parse_next(input) /// # } /// ``` /// /// # 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> { /// 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>> { /// 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream + Compare> + Compare + AsBStr, ::Slice: ParseSlice, ::Token: AsChar + Clone, ::IterOffsets: Clone, Error: ParserError, { trace("float", move |input: &mut Input| { let s = take_float_or_exceptions(input)?; s.parse_slice() .ok_or_else(|| ErrMode::from_error_kind(input, ErrorKind::Verify)) }) .parse_next(input) } #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug fn take_float_or_exceptions>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare>, I: Compare, ::Token: AsChar + Clone, ::IterOffsets: Clone, I: AsBStr, { alt(( take_float, crate::token::literal(Caseless("nan")), ( opt(one_of(['+', '-'])), crate::token::literal(Caseless("infinity")), ) .take(), ( opt(one_of(['+', '-'])), crate::token::literal(Caseless("inf")), ) .take(), )) .parse_next(input) } #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug fn take_float>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare, ::Token: AsChar + Clone, ::IterOffsets: Clone, I: AsBStr, { ( opt(one_of(['+', '-'])), alt(( (digit1, opt(('.', opt(digit1)))).void(), ('.', digit1).void(), )), opt((one_of(['e', 'E']), opt(one_of(['+', '-'])), cut_err(digit1))), ) .take() .parse_next(input) } /// Recognize the input slice with escaped characters. /// /// Arguments: /// - `normal`: unescapeable characters /// - Must not include `control` /// - `control_char`: e.g. `\` for strings in most languages /// - `escape`: parse and transform the escaped character /// /// Parsing ends when: /// - `alt(normal, control._char)` [`Backtrack`s][crate::error::ErrMode::Backtrack] /// - `normal` doesn't advance the input stream /// - *(complete)* input stream is exhausted /// /// See also [`escaped_transform`] /// /// # Example /// /// ```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::take_escaped; /// use winnow::token::one_of; /// /// fn esc(s: &str) -> IResult<&str, &str> { /// take_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::take_escaped; /// use winnow::token::one_of; /// /// fn esc(s: Partial<&str>) -> IResult, &str> { /// take_escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s) /// } /// /// assert_eq!(esc(Partial::new("123;")), Ok((Partial::new(";"), "123"))); /// assert_eq!(esc(Partial::new("12\\\"34;")), Ok((Partial::new(";"), "12\\\"34"))); /// ``` #[inline(always)] pub fn take_escaped<'i, Input, Error, Normal, Escapable, NormalOutput, EscapableOutput>( mut normal: Normal, control_char: char, mut escapable: Escapable, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream + Compare + 'i, Normal: Parser, Escapable: Parser, Error: ParserError, { trace("take_escaped", move |input: &mut Input| { if ::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) } }) } /// Deprecated, replaced with [`take_escaped`] #[deprecated(since = "0.6.4", note = "Replaced with `take_escaped`")] #[inline(always)] pub fn escaped<'i, Input, Error, Normal, Escapable, NormalOutput, EscapableOutput>( normal: Normal, control_char: char, escapable: Escapable, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream + Compare + 'i, Normal: Parser, Escapable: Parser, Error: ParserError, { take_escaped(normal, control_char, escapable) } fn streaming_escaped_internal( input: &mut I, normal: &mut F, control_char: char, escapable: &mut G, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, I: Compare, F: Parser, G: Parser, Error: ParserError, { 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, Error, F, G, O1, O2>( input: &mut I, normal: &mut F, control_char: char, escapable: &mut G, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, I: Compare, I: 'a, F: Parser, G: Parser, Error: ParserError, { 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()) } /// Parse escaped characters, unescaping them /// /// Arguments: /// - `normal`: unescapeable characters /// - Must not include `control` /// - `control_char`: e.g. `\` for strings in most languages /// - `escape`: parse and transform the escaped character /// /// Parsing ends when: /// - `alt(normal, control._char)` [`Backtrack`s][crate::error::ErrMode::Backtrack] /// - `normal` doesn't advance the input stream /// - *(complete)* input stream is exhausted /// /// # Example /// /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use std::str::from_utf8; /// use winnow::token::literal; /// use winnow::ascii::escaped_transform; /// use winnow::ascii::alpha1; /// use winnow::combinator::alt; /// /// fn parser<'s>(input: &mut &'s str) -> PResult> { /// 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")))); /// # } /// ``` /// /// ``` /// # #[cfg(feature = "std")] { /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use std::str::from_utf8; /// # use winnow::Partial; /// use winnow::token::literal; /// use winnow::ascii::escaped_transform; /// use winnow::ascii::alpha1; /// use winnow::combinator::alt; /// /// fn parser<'s>(input: &mut Partial<&'s str>) -> PResult>> { /// 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( mut normal: Normal, control_char: char, mut escape: Escape, ) -> impl Parser where Input: StreamIsPartial + Stream + Compare, Output: crate::stream::Accumulate<::Slice>, Normal: Parser::Slice, Error>, Escape: Parser::Slice, Error>, Error: ParserError, { trace("escaped_transform", move |input: &mut Input| { if ::is_partial_supported() && input.is_partial() { streaming_escaped_transform_internal(input, &mut normal, control_char, &mut escape) } else { complete_escaped_transform_internal(input, &mut normal, control_char, &mut escape) } }) } fn streaming_escaped_transform_internal( input: &mut I, normal: &mut F, control_char: char, transform: &mut G, ) -> PResult where I: StreamIsPartial, I: Stream, I: Compare, Output: crate::stream::Accumulate<::Slice>, F: Parser::Slice, Error>, G: Parser::Slice, Error>, Error: ParserError, { 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( input: &mut I, normal: &mut F, control_char: char, transform: &mut G, ) -> PResult where I: StreamIsPartial, I: Stream, I: Compare, Output: crate::stream::Accumulate<::Slice>, F: Parser::Slice, Error>, G: Parser::Slice, Error>, Error: ParserError, { 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; } winnow-0.6.26/src/ascii/tests.rs000064400000000000000000001435421046102023000146400ustar 00000000000000use super::*; use crate::prelude::*; mod complete { use super::*; use crate::combinator::alt; 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_till_line_ending_bytes() { let a: &[u8] = b"ab12cd\nefgh"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(a), Ok((&b"\nefgh"[..], &b"ab12cd"[..])) ); let b: &[u8] = b"ab12cd\nefgh\nijkl"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(b), Ok((&b"\nefgh\nijkl"[..], &b"ab12cd"[..])) ); let c: &[u8] = b"ab12cd\r\nefgh\nijkl"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(c), Ok((&b"\r\nefgh\nijkl"[..], &b"ab12cd"[..])) ); let d: &[u8] = b"ab12cd"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(d), Ok((&[][..], d)) ); } #[test] fn is_till_line_ending_str() { let f = "βèƒôřè\rÂßÇáƒƭèř"; assert_eq!( till_line_ending.parse_peek(f), Err(ErrMode::Backtrack(InputError::new( &f[12..], ErrorKind::Tag ))) ); let g2: &str = "ab12cd"; assert_eq!( till_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])> { (till_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])> { (till_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))) ); } #[test] fn dec_uint_tests() { fn dec_u32(input: &[u8]) -> IResult<&[u8], u32> { dec_uint.parse_peek(input) } assert_parse!( dec_u32(&b";"[..]), Err(ErrMode::Backtrack(error_position!( &&b";"[..], ErrorKind::Verify ))) ); assert_parse!(dec_u32(&b"0;"[..]), Ok((&b";"[..], 0))); assert_parse!(dec_u32(&b"1;"[..]), Ok((&b";"[..], 1))); assert_parse!(dec_u32(&b"32;"[..]), Ok((&b";"[..], 32))); assert_parse!( dec_u32(&b"1000000000000000000000;"[..]), // overflow Err(ErrMode::Backtrack(error_position!( &&b"1000000000000000000000;"[..], ErrorKind::Verify ))) ); } #[test] fn dec_int_tests() { fn dec_i32(input: &[u8]) -> IResult<&[u8], i32> { dec_int.parse_peek(input) } assert_parse!( dec_i32(&b";"[..]), Err(ErrMode::Backtrack(error_position!( &&b";"[..], ErrorKind::Verify ))) ); assert_parse!(dec_i32(&b"0;"[..]), Ok((&b";"[..], 0))); assert_parse!(dec_i32(&b"1;"[..]), Ok((&b";"[..], 1))); assert_parse!(dec_i32(&b"32;"[..]), Ok((&b";"[..], 32))); assert_parse!( dec_i32(&b"-0;"[..]), Err(ErrMode::Backtrack(error_position!( &&b"-0;"[..], ErrorKind::Verify ))) ); assert_parse!(dec_i32(&b"-1;"[..]), Ok((&b";"[..], -1))); assert_parse!(dec_i32(&b"-32;"[..]), Ok((&b";"[..], -32))); assert_parse!( dec_i32(&b"1000000000000000000000;"[..]), // overflow Err(ErrMode::Backtrack(error_position!( &&b"1000000000000000000000;"[..], ErrorKind::Verify ))) ); } #[test] 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 test_cases = [ "+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", "inf", "Inf", "infinity", "Infinity", "-inf", "-Inf", "-infinity", "-Infinity", "+inf", "+Inf", "+infinity", "+Infinity", ]; for test in test_cases { let expected32 = str::parse::(test).unwrap(); let expected64 = str::parse::(test).unwrap(); println!("now parsing: {test} -> {expected32}"); assert_parse!( float.parse_peek(test.as_bytes()), Ok((&b""[..], expected32)) ); assert_parse!(float.parse_peek(test), Ok(("", expected32))); assert_parse!( float.parse_peek(test.as_bytes()), Ok((&b""[..], expected64)) ); assert_parse!(float.parse_peek(test), Ok(("", expected64))); } let remaining_exponent = "-1.234E-"; assert_parse!( float::<_, f64, _>.parse_peek(remaining_exponent), Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice))) ); let nan_test_cases = ["nan", "NaN", "NAN"]; for test in nan_test_cases { println!("now parsing: {test}"); let (remaining, parsed) = float::<_, f32, ()>.parse_peek(test.as_bytes()).unwrap(); assert!(parsed.is_nan()); assert!(remaining.is_empty()); let (remaining, parsed) = float::<_, f32, ()>.parse_peek(test).unwrap(); assert!(parsed.is_nan()); assert!(remaining.is_empty()); let (remaining, parsed) = float::<_, f64, ()>.parse_peek(test.as_bytes()).unwrap(); assert!(parsed.is_nan()); assert!(remaining.is_empty()); let (remaining, parsed) = float::<_, f64, ()>.parse_peek(test).unwrap(); assert!(parsed.is_nan()); assert!(remaining.is_empty()); } } #[cfg(feature = "std")] fn parse_f64(i: &str) -> IResult<&str, f64, ()> { match take_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 "take_escaped hangs if normal parser accepts empty" #[test] fn complete_take_escaped_hang() { // issue #1336 "take_escaped hangs if normal parser accepts empty" fn escaped_string(input: &str) -> IResult<&str, &str> { use crate::ascii::alpha0; use crate::token::one_of; take_escaped(alpha0, '\\', one_of(['n'])).parse_peek(input) } escaped_string("7").unwrap(); escaped_string("a7").unwrap(); } #[test] fn complete_take_escaped_hang_1118() { // issue ##1118 take_escaped does not work with empty string fn unquote(input: &str) -> IResult<&str, &str> { use crate::combinator::delimited; use crate::combinator::opt; use crate::token::one_of; delimited( '"', take_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]> { take_escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) } assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], &b"abcd"[..]))); assert_eq!(esc(&b"ab\\\"cd;"[..]), Ok((&b";"[..], &b"ab\\\"cd"[..]))); 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]> { take_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> { take_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> { take_escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) } assert_eq!(esc2("12\\nnn34"), Ok(("nn34", "12\\n"))); fn esc3(i: &str) -> IResult<&str, &str> { take_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; take_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) -> 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::error::ErrorKind; use crate::error::InputError; use crate::error::{ErrMode, Needed}; use crate::IResult; use crate::Partial; 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_till_line_ending_bytes() { let a: &[u8] = b"ab12cd\nefgh"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(Partial::new(a)), Ok((Partial::new(&b"\nefgh"[..]), &b"ab12cd"[..])) ); let b: &[u8] = b"ab12cd\nefgh\nijkl"; assert_eq!( till_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!( till_line_ending::<_, InputError<_>>.parse_peek(Partial::new(c)), Ok((Partial::new(&b"\r\nefgh\nijkl"[..]), &b"ab12cd"[..])) ); let d: &[u8] = b"ab12cd"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(Partial::new(d)), Err(ErrMode::Incomplete(Needed::Unknown)) ); } #[test] fn is_till_line_ending_str() { let f = "βèƒôřè\rÂßÇáƒƭèř"; assert_eq!( till_line_ending.parse_peek(Partial::new(f)), Err(ErrMode::Backtrack(InputError::new( Partial::new(&f[12..]), ErrorKind::Tag ))) ); let g2: &str = "ab12cd"; assert_eq!( till_line_ending::<_, InputError<_>>.parse_peek(Partial::new(g2)), Err(ErrMode::Incomplete(Needed::Unknown)) ); } #[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, (&[u8], &[u8])> { (till_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, (&[u8], &[u8])> { (till_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 ))) ); } #[test] fn hex_uint_tests() { fn hex_u32(input: Partial<&[u8]>) -> IResult, 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))) ); } } winnow-0.6.26/src/binary/bits/mod.rs000064400000000000000000000315431046102023000154070ustar 00000000000000//! Bit level parsers //! #[cfg(test)] mod tests; use crate::combinator::trace; use crate::error::{ErrMode, ErrorConvert, ErrorKind, Needed, ParserError}; use crate::lib::std::ops::{AddAssign, Div, Shl, Shr}; use crate::stream::{Stream, StreamIsPartial, ToUsize}; use crate::{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, (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( mut parser: ParseNext, ) -> impl Parser where BitError: ParserError<(Input, usize)> + ErrorConvert, ByteError: ParserError, (Input, usize): Stream, Input: Stream + Clone, ParseNext: Parser<(Input, usize), Output, BitError>, { trace("bits", move |input: &mut Input| { let mut bit_input = (input.clone(), 0); match parser.parse_next(&mut bit_input) { Ok(result) => { let (mut rest, offset) = bit_input; // If the next byte has been partially read, it will be sliced away as well. // The parser functions might already slice away all fully read bytes. // That's why `offset / BYTE` isn't necessarily needed at all times. let remaining_bytes_index = offset / BYTE + if offset % BYTE == 0 { 0 } else { 1 }; let _ = rest.next_slice(remaining_bytes_index); *input = rest; Ok(result) } Err(ErrMode::Incomplete(n)) => Err(ErrMode::Incomplete(n.map(|u| u.get() / BYTE + 1))), Err(e) => Err(e.convert()), } }) } /// Convert a [`bits`] stream back into a byte stream /// ///
/// /// **Warning:** A partial byte remaining in the input will be ignored and the given parser will /// start parsing at the next full byte. /// ///
/// /// # Examples /// /// ``` /// use winnow::prelude::*; /// use winnow::Bytes; /// use winnow::binary::bits::{bits, bytes, take}; /// use winnow::token::rest; /// use winnow::error::InputError; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parse(input: Stream<'_>) -> IResult, (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( mut parser: ParseNext, ) -> impl Parser<(Input, usize), Output, BitError> where ByteError: ParserError + ErrorConvert, BitError: ParserError<(Input, usize)>, Input: Stream + Clone, ParseNext: Parser, { trace("bytes", move |bit_input: &mut (Input, usize)| { let (mut input, offset) = bit_input.clone(); let _ = if offset % BYTE != 0 { input.next_slice(1 + offset / BYTE) } else { input.next_slice(offset / BYTE) }; match parser.parse_next(&mut input) { Ok(res) => { *bit_input = (input, 0); Ok(res) } Err(ErrMode::Incomplete(Needed::Unknown)) => Err(ErrMode::Incomplete(Needed::Unknown)), Err(ErrMode::Incomplete(Needed::Size(sz))) => Err(match sz.get().checked_mul(BYTE) { Some(v) => ErrMode::Incomplete(Needed::new(v)), None => ErrMode::Cut(BitError::assert( bit_input, "overflow in turning needed bytes into needed bits", )), }), Err(e) => Err(e.convert()), } }) } /// Parse taking `count` bits /// /// # Effective Signature /// /// Assuming you are parsing a `(&[u8], usize)` bit [Stream]: /// ```rust /// # use winnow::prelude::*;; /// # use winnow::error::ContextError; /// pub fn take<'i>(count: usize) -> impl Parser<(&'i [u8], usize), u8, ContextError> /// # { /// # winnow::binary::bits::take(count) /// # } /// ``` /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # 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(count: Count) -> impl Parser<(Input, usize), Output, Error> where Input: Stream + StreamIsPartial + Clone, Output: From + AddAssign + Shl + Shr, Count: ToUsize, Error: ParserError<(Input, usize)>, { let count = count.to_usize(); trace("take", move |input: &mut (Input, usize)| { if ::is_partial_supported() { take_::<_, _, _, true>(input, count) } else { take_::<_, _, _, false>(input, count) } }) } fn take_, const PARTIAL: bool>( bit_input: &mut (I, usize), count: usize, ) -> PResult where I: StreamIsPartial, I: Stream + Clone, O: From + AddAssign + Shl + Shr, { if count == 0 { Ok(0u8.into()) } else { let (mut input, bit_offset) = bit_input.clone(); if input.eof_offset() * BYTE < count + bit_offset { if PARTIAL && input.is_partial() { Err(ErrMode::Incomplete(Needed::new(count))) } 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.iter_offsets().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.next_slice(cnt); *bit_input = (input, end_offset); Ok(acc) } } } /// Parse taking `count` bits and comparing them to `pattern` /// /// # Effective Signature /// /// Assuming you are parsing a `(&[u8], usize)` bit [Stream]: /// ```rust /// # use winnow::prelude::*;; /// # use winnow::error::ContextError; /// pub fn pattern<'i>(pattern: u8, count: usize) -> impl Parser<(&'i [u8], usize), u8, ContextError> /// # { /// # winnow::binary::bits::pattern(pattern, count) /// # } /// ``` /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::Bytes; /// # use winnow::error::{InputError, ErrorKind}; /// use winnow::binary::bits::pattern; /// /// 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(bits: u8, count: u8, input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), u8> { /// pattern(bits, count).parse_peek(input) /// } /// /// // The lowest 4 bits of 0b00001111 match the lowest 4 bits of 0b11111111. /// 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")] #[doc(alias = "tag")] pub fn pattern>( pattern: Output, count: Count, ) -> impl Parser<(Input, usize), Output, Error> where Input: Stream + StreamIsPartial + Clone, Count: ToUsize, Output: From + AddAssign + Shl + Shr + PartialEq, { let count = count.to_usize(); trace("pattern", move |input: &mut (Input, usize)| { let start = input.checkpoint(); take(count).parse_next(input).and_then(|o| { if pattern == o { Ok(o) } else { input.reset(&start); Err(ErrMode::Backtrack(Error::from_error_kind( input, ErrorKind::Tag, ))) } }) }) } /// Parses one specific bit as a bool. /// /// # Effective Signature /// /// Assuming you are parsing a `(&[u8], usize)` bit [Stream]: /// ```rust /// # use winnow::prelude::*;; /// # use winnow::error::ContextError; /// pub fn bool(input: &mut (&[u8], usize)) -> PResult /// # { /// # winnow::binary::bits::bool.parse_next(input) /// # } /// ``` /// /// # 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>( input: &mut (Input, usize), ) -> PResult where Input: Stream + StreamIsPartial + Clone, { trace("bool", |input: &mut (Input, usize)| { let bit: u32 = take(1usize).parse_next(input)?; Ok(bit != 0) }) .parse_next(input) } winnow-0.6.26/src/binary/bits/tests.rs000064400000000000000000000123341046102023000157670ustar 00000000000000use super::*; use crate::error::IResult; 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 more data", error.to_string()); } #[test] fn test_take_complete_0() { let input = &[0b00010010][..]; let count = 0usize; assert_eq!(count, 0usize); let offset = 0usize; let result: 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: 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: 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: IResult<(_, usize), usize> = take(count).parse_peek((input, offset)); assert_eq!(result, Ok(((input, offset), 0))); } #[test] fn test_pattern_partial_ok() { let input = Partial::new(&[0b00011111][..]); let offset = 0usize; let bits_to_take = 4usize; let value_to_pattern = 0b0001; let result: IResult<(_, usize), usize> = pattern(value_to_pattern, bits_to_take).parse_peek((input, offset)); assert_eq!(result, Ok(((input, bits_to_take), value_to_pattern))); } #[test] fn test_pattern_partial_err() { let input = Partial::new(&[0b00011111][..]); let offset = 0usize; let bits_to_take = 4usize; let value_to_pattern = 0b1111; let result: IResult<(_, usize), usize> = pattern(value_to_pattern, 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: 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: 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: 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: IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 8)); assert_eq!( result, Err(crate::error::ErrMode::Incomplete(Needed::new(1))) ); } winnow-0.6.26/src/binary/mod.rs000064400000000000000000002617241046102023000144540ustar 00000000000000//! Parsers recognizing numbers #![allow(clippy::match_same_arms)] pub mod bits; #[cfg(test)] mod tests; use crate::combinator::repeat; use crate::combinator::trace; 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::{Stream, StreamIsPartial}; use crate::stream::{ToUsize, UpdateSlice}; 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { u8(input) } /// Recognizes a big endian unsigned 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_u16", move |input: &mut Input| be_uint(input, 2)).parse_next(input) } /// Recognizes a big endian unsigned 3 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_u23", move |input: &mut Input| be_uint(input, 3)).parse_next(input) } /// Recognizes a big endian unsigned 4 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_u32", move |input: &mut Input| be_uint(input, 4)).parse_next(input) } /// Recognizes a big endian unsigned 8 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_u64", move |input: &mut Input| be_uint(input, 8)).parse_next(input) } /// Recognizes a big endian unsigned 16 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_u128", move |input: &mut Input| be_uint(input, 16)).parse_next(input) } #[inline] fn be_uint(input: &mut Input, bound: usize) -> PResult where Input: StreamIsPartial + Stream, Uint: Default + Shl + Add + From, Error: ParserError, { debug_assert_ne!(bound, 1, "to_be_uint needs extra work to avoid overflow"); match input.offset_at(bound) { Ok(offset) => { let res = to_be_uint(input, offset); input.next_slice(offset); Ok(res) } Err(e) if ::is_partial_supported() && input.is_partial() => { Err(ErrMode::Incomplete(e)) } Err(_needed) => Err(ErrMode::from_error_kind(input, ErrorKind::Slice)), } } #[inline] fn to_be_uint(number: &Input, offset: usize) -> Uint where Input: Stream, Uint: Default + Shl + Add + From<::Token>, { let mut res = Uint::default(); for (_, byte) in number.iter_offsets().take(offset) { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { i8(input) } /// Recognizes a big endian signed 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_i16", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_i24", move |input: &mut Input| { be_uint::<_, u32, _>(input, 3).map(|n| { // Same as the unsigned version but we need to sign-extend manually here let n = if n & 0x80_00_00 != 0 { (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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_i32", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_i64", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_i128", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { u8(input) } /// Recognizes a little endian unsigned 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_u16", move |input: &mut Input| le_uint(input, 2)).parse_next(input) } /// Recognizes a little endian unsigned 3 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_u24", move |input: &mut Input| le_uint(input, 3)).parse_next(input) } /// Recognizes a little endian unsigned 4 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_u32", move |input: &mut Input| le_uint(input, 4)).parse_next(input) } /// Recognizes a little endian unsigned 8 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_u64", move |input: &mut Input| le_uint(input, 8)).parse_next(input) } /// Recognizes a little endian unsigned 16 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_u128", move |input: &mut Input| le_uint(input, 16)).parse_next(input) } #[inline] fn le_uint(input: &mut Input, bound: usize) -> PResult where Input: StreamIsPartial + Stream, Uint: Default + Shl + Add + From, Error: ParserError, { match input.offset_at(bound) { Ok(offset) => { let res = to_le_uint(input, offset); input.next_slice(offset); Ok(res) } Err(e) if ::is_partial_supported() && input.is_partial() => { Err(ErrMode::Incomplete(e)) } Err(_needed) => Err(ErrMode::from_error_kind(input, ErrorKind::Slice)), } } #[inline] fn to_le_uint(number: &Input, offset: usize) -> Uint where Input: Stream, Uint: Default + Shl + Add + From<::Token>, { let mut res = Uint::default(); for (index, byte) in number.iter_offsets().take(offset) { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { i8(input) } /// Recognizes a little endian signed 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_i16", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_i24", move |input: &mut Input| { le_uint::<_, u32, _>(input, 3).map(|n| { // Same as the unsigned version but we need to sign-extend manually here let n = if n & 0x80_00_00 != 0 { (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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_i32", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_i64", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_i128", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("u8", move |input: &mut Input| { if ::is_partial_supported() { u8_::<_, _, true>(input) } else { u8_::<_, _, false>(input) } }) .parse_next(input) } fn u8_(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { input.next_token().ok_or_else(|| { if PARTIAL && input.is_partial() { ErrMode::Incomplete(Needed::new(1)) } else { ErrMode::Backtrack(Error::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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("i8", move |input: &mut Input| { if ::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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_f32", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_f64", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("le_f32", move |input: &mut Input| { 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][crate::_topic::partial]*: 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, 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(input: &mut Input) -> PResult where Input: StreamIsPartial + Stream, Error: ParserError, { trace("be_f64", move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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][crate::_topic::partial]*: 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(endian: Endianness) -> impl Parser where Input: StreamIsPartial + Stream, Error: ParserError, { move |input: &mut Input| { 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) } /// Get a length-prefixed slice ([TLV](https://en.wikipedia.org/wiki/Type-length-value)) /// /// To apply a parser to the returned slice, see [`length_and_then`]. /// /// If the count is for something besides tokens, see [`length_repeat`]. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # 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_take; /// /// type Stream<'i> = Partial<&'i Bytes>; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Partial::new(Bytes::new(b)) /// } /// /// fn parser(s: Stream<'_>) -> IResult, &[u8]> { /// length_take(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_take( mut count: CountParser, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream, Count: ToUsize, CountParser: Parser, Error: ParserError, { trace("length_take", move |i: &mut Input| { let length = count.parse_next(i)?; crate::token::take(length).parse_next(i) }) } /// Parse a length-prefixed slice ([TLV](https://en.wikipedia.org/wiki/Type-length-value)) /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # 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_and_then; /// /// 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, &[u8]> { /// length_and_then(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_and_then( mut count: CountParser, mut parser: ParseNext, ) -> impl Parser where Input: StreamIsPartial + Stream + UpdateSlice + Clone, Count: ToUsize, CountParser: Parser, ParseNext: Parser, Error: ParserError, { trace("length_and_then", move |i: &mut Input| { let data = length_take(count.by_ref()).parse_next(i)?; let mut data = Input::update_slice(i.clone(), data); let _ = data.complete(); let o = parser.by_ref().complete_err().parse_next(&mut data)?; Ok(o) }) } /// [`Accumulate`] a length-prefixed sequence of values ([TLV](https://en.wikipedia.org/wiki/Type-length-value)) /// /// If the length represents token counts, see instead [`length_take`] /// /// # 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_repeat; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parser(s: Stream<'_>) -> IResult, Vec<&[u8]>> { /// length_repeat(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_repeat( mut count: CountParser, mut parser: ParseNext, ) -> impl Parser where Input: Stream, Count: ToUsize, Accumulator: Accumulate, CountParser: Parser, ParseNext: Parser, Error: ParserError, { trace("length_repeat", move |i: &mut Input| { let n = count.parse_next(i)?; let n = n.to_usize(); repeat(n, parser.by_ref()).parse_next(i) }) } winnow-0.6.26/src/binary/tests.rs000064400000000000000000001252601046102023000150310ustar 00000000000000use 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_u16_tests() { assert_parse!( le_u16.parse_peek(Partial::new(&[0x00, 0x03][..])), Ok((Partial::new(&b""[..]), 0x0300)) ); assert_parse!( le_u16.parse_peek(Partial::new(&[b'a', b'b'][..])), Ok((Partial::new(&b""[..]), 0x6261)) ); assert_parse!( le_u16.parse_peek(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(1))) ); } #[test] fn le_i8_tests() { assert_parse!( le_i8.parse_peek(Partial::new(&[0x00][..])), 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_u32_test() { assert_parse!( le_u32.parse_peek(Partial::new(&[0x00, 0x03, 0x05, 0x07][..])), Ok((Partial::new(&b""[..]), 0x07050300)) ); assert_parse!( le_u32.parse_peek(Partial::new(&[b'a', b'b', b'c', b'd'][..])), Ok((Partial::new(&b""[..]), 0x64636261)) ); assert_parse!( le_u32.parse_peek(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(3))) ); } #[test] fn le_i32_tests() { assert_parse!( le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), 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, u16> { u16(Endianness::Big).parse_peek(i) } fn le_tst16(i: Partial<&[u8]>) -> IResult, 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, u32> { u32(Endianness::Big).parse_peek(i) } fn le_tst32(i: Partial<&[u8]>) -> IResult, 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, u64> { u64(Endianness::Big).parse_peek(i) } fn le_tst64(i: Partial<&[u8]>) -> IResult, 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, i16> { i16(Endianness::Big).parse_peek(i) } fn le_tsti16(i: Partial<&[u8]>) -> IResult, 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, i32> { i32(Endianness::Big).parse_peek(i) } fn le_tsti32(i: Partial<&[u8]>) -> IResult, 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, i64> { i64(Endianness::Big).parse_peek(i) } fn le_tsti64(i: Partial<&[u8]>) -> IResult, 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_repeat_test() { fn number(i: Partial<&[u8]>) -> IResult, u32> { digit .try_map(str::from_utf8) .try_map(FromStr::from_str) .parse_peek(i) } fn cnt(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { length_repeat(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 partial_length_bytes() { use crate::binary::le_u8; fn x(i: Partial<&[u8]>) -> IResult, &[u8]> { length_take(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, &[u8]> { let (i, _) = "magic".parse_peek(i)?; length_take(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))) ); } #[test] fn length_take_test() { fn number(i: Partial<&[u8]>) -> IResult, u32> { digit .try_map(str::from_utf8) .try_map(FromStr::from_str) .parse_peek(i) } fn take(i: Partial<&[u8]>) -> IResult, &[u8]> { length_take(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_and_then_test() { use crate::stream::StreamIsPartial; fn length_and_then_1(i: Partial<&[u8]>) -> IResult, u16> { length_and_then(be_u8, be_u16).parse_peek(i) } fn length_and_then_2(i: Partial<&[u8]>) -> IResult, (u8, u8)> { length_and_then(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_and_then_1(Partial::new(&i1)), Err(ErrMode::Backtrack(error_position!( &empty_complete, ErrorKind::Slice ))) ); assert_eq!( length_and_then_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_and_then_1(Partial::new(&i2)), Err(ErrMode::Backtrack(error_position!( &middle_complete, ErrorKind::Slice ))) ); assert_eq!( length_and_then_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_and_then_1(Partial::new(&i3)), Ok((Partial::new(&i3[3..]), 1286)) ); assert_eq!( length_and_then_2(Partial::new(&i3)), Ok((Partial::new(&i3[3..]), (5, 6))) ); let i4 = [3, 5, 6, 3, 4, 5]; assert_eq!( length_and_then_1(Partial::new(&i4)), Ok((Partial::new(&i4[4..]), 1286)) ); assert_eq!( length_and_then_2(Partial::new(&i4)), Ok((Partial::new(&i4[4..]), (5, 6))) ); } } winnow-0.6.26/src/combinator/branch.rs000064400000000000000000000301161046102023000157700ustar 00000000000000use crate::combinator::trace; use crate::error::{ErrMode, ErrorKind, ParserError}; use crate::stream::Stream; 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 { /// Tests each parser in the tuple and returns the result of the first one that succeeds fn choice(&mut self, input: &mut I) -> PResult; } /// Pick the first successful parser /// /// To stop on an error, rather than trying further cases, see /// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]). /// /// For tight control over the error when no match is found, add a final case using [`fail`][crate::combinator::fail]. /// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all /// 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, takes 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")] #[inline(always)] pub fn alt( mut alternatives: Alternatives, ) -> impl Parser where Alternatives: Alt, Error: ParserError, { trace("alt", move |i: &mut Input| alternatives.choice(i)) } /// Helper trait for the [`permutation()`] combinator. /// /// This trait is implemented for tuples of up to 21 elements pub trait Permutation { /// Tries to apply all parsers in the tuple in various orders until all of them succeed fn permutation(&mut self, input: &mut I) -> PResult; } /// 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. /// /// To stop on an error, rather than trying further permutations, see /// [`cut_err`][crate::combinator::cut_err] ([example][crate::_tutorial::chapter_7]). /// /// # Example /// /// ```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 takes 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::Tag)))); /// ``` /// #[inline(always)] pub fn permutation, List: Permutation>( mut l: List, ) -> impl Parser { trace("permutation", move |i: &mut I| l.permutation(i)) } impl, P: Parser> Alt for [P; N] { fn choice(&mut self, input: &mut I) -> PResult { let mut error: Option = None; let start = input.checkpoint(); for branch in self { input.reset(&start); match branch.parse_next(input) { Err(ErrMode::Backtrack(e)) => { error = match error { Some(error) => Some(error.or(e)), None => Some(e), }; } res => return res, } } match error { Some(e) => Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Alt))), None => Err(ErrMode::assert(input, "`alt` needs at least one parser")), } } } impl, P: Parser> Alt for &mut [P] { fn choice(&mut self, input: &mut I) -> PResult { let mut error: Option = None; let start = input.checkpoint(); for branch in self.iter_mut() { input.reset(&start); match branch.parse_next(input) { Err(ErrMode::Backtrack(e)) => { error = match error { Some(error) => Some(error.or(e)), None => Some(e), }; } res => return res, } } match error { Some(e) => Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Alt))), None => Err(ErrMode::assert(input, "`alt` needs at least one parser")), } } } 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, $($id: Parser),+ > Alt for ( $($id),+ ) { fn choice(&mut self, input: &mut I) -> PResult { 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! 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)*)); ); macro_rules! alt_trait_inner( ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({ $input.reset(&$start); 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, &$start, ErrorKind::Alt))) }); ); alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10 Alt11 Alt12 Alt13 Alt14 Alt15 Alt16 Alt17 Alt18 Alt19 Alt20 Alt21 Alt22); // Manually implement Alt for (A,), the 1-tuple type impl, A: Parser> Alt for (A,) { fn choice(&mut self, input: &mut I) -> PResult { 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, $($name: Parser),+ > Permutation for ( $($name),+ ) { fn permutation(&mut self, input: &mut I) -> PResult<( $($ty),+ ), Error> { let mut res = ($(Option::<$ty>::None),+); loop { let mut err: Option = 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); return Err(ErrMode::Backtrack(err.append(input, &start, 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); 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 ); winnow-0.6.26/src/combinator/core.rs000064400000000000000000000342001046102023000154610ustar 00000000000000use crate::combinator::trace; use crate::error::{ErrMode, ErrorKind, Needed, ParserError}; use crate::stream::Stream; use crate::*; /// Deprecated, replaced with [`token::rest`] #[deprecated(since = "0.6.23", note = "replaced with `token::rest`")] #[inline] pub fn rest(input: &mut Input) -> PResult<::Slice, Error> where Input: Stream, Error: ParserError, { crate::token::rest(input) } /// Deprecated, replaced with [`token::rest_len`] #[deprecated(since = "0.6.23", note = "replaced with `token::rest_len`")] #[inline] pub fn rest_len(input: &mut Input) -> PResult where Input: Stream, Error: ParserError, { crate::token::rest_len(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( mut parser: ParseNext, ) -> impl Parser, Error> where ParseNext: Parser, Error: ParserError, { trace("opt", move |input: &mut Input| { let start = input.checkpoint(); match parser.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( cond: bool, mut parser: ParseNext, ) -> impl Parser, Error> where Input: Stream, ParseNext: Parser, Error: ParserError, { trace("cond", move |input: &mut Input| { if cond { parser.parse_next(input).map(Some) } else { Ok(None) } }) } /// Apply the parser without advancing the input. /// /// To lookahead and only advance on success, see [`opt`]. /// /// # 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( mut parser: ParseNext, ) -> impl Parser where Input: Stream, Error: ParserError, ParseNext: Parser, { trace("peek", move |input: &mut Input| { let start = input.checkpoint(); let res = parser.parse_next(input); input.reset(&start); res }) } /// Match the end of the [`Stream`] /// /// Otherwise, it will error. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn eof<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::combinator::eof.parse_next(input) /// # } /// ``` /// /// # Example /// /// ```rust /// # 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(input: &mut Input) -> PResult<::Slice, Error> where Input: Stream, Error: ParserError, { trace("eof", move |input: &mut Input| { 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(mut parser: ParseNext) -> impl Parser where Input: Stream, Error: ParserError, ParseNext: Parser, { trace("not", move |input: &mut Input| { 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]. /// /// See the [tutorial][crate::_tutorial::chapter_7] for more details. /// /// # Example /// /// Without `cut_err`: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::token::one_of; /// # use winnow::token::rest; /// # use winnow::ascii::digit1; /// # use winnow::combinator::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::token::rest; /// # use winnow::ascii::digit1; /// # 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( mut parser: ParseNext, ) -> impl Parser where Input: Stream, Error: ParserError, ParseNext: Parser, { trace("cut_err", move |input: &mut Input| { 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( mut parser: ParseNext, ) -> impl Parser where Input: Stream, Error: ParserError, ParseNext: Parser, { trace("backtrack_err", move |input: &mut Input| { 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 { /// todo(input) /// } /// ``` #[track_caller] pub fn todo(input: &mut Input) -> PResult where Input: Stream, { #![allow(clippy::todo)] trace("todo", move |_input: &mut Input| { 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, 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::>(); /// 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( input: Input, parser: ParseNext, ) -> ParserIterator where ParseNext: Parser, Input: Stream, Error: ParserError, { ParserIterator { parser, input, state: Some(State::Running), o: Default::default(), } } /// Main structure associated to [`iterator`]. pub struct ParserIterator where F: Parser, I: Stream, { parser: F, input: I, state: Option>, o: core::marker::PhantomData, } impl ParserIterator where F: Parser, 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 core::iter::Iterator for &mut ParserIterator where F: Parser, I: Stream, { type Item = O; fn next(&mut self) -> Option { 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 { Running, Done, Failure(E), Incomplete(Needed), } /// Succeed, consuming no input /// /// For example, it can be used as the last alternative in `alt` to /// specify the default case. /// /// Useful with: /// - [`Parser::value`] /// - [`Parser::default_value`] /// - [`Parser::map`] /// ///
/// /// **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::empty; /// /// fn sign(input: &str) -> IResult<&str, isize> { /// alt(( /// '-'.value(-1), /// '+'.value(1), /// empty.value(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 = "success")] #[inline] pub fn empty(_input: &mut Input) -> PResult<(), Error> where Input: Stream, Error: ParserError, { Ok(()) } /// 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")] #[inline] pub fn fail(i: &mut Input) -> PResult where Input: Stream, Error: ParserError, { trace("fail", |i: &mut Input| { Err(ErrMode::from_error_kind(i, ErrorKind::Fail)) }) .parse_next(i) } winnow-0.6.26/src/combinator/debug/internals.rs000064400000000000000000000201441046102023000176200ustar 00000000000000#![cfg(feature = "std")] use std::io::Write; use crate::error::ErrMode; use crate::stream::Stream; use crate::*; pub(crate) struct Trace where P: Parser, I: Stream, D: std::fmt::Display, { parser: P, name: D, call_count: usize, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl Trace where P: Parser, I: Stream, D: std::fmt::Display, { #[inline(always)] pub(crate) fn new(parser: P, name: D) -> Self { Self { parser, name, call_count: 0, i: Default::default(), o: Default::default(), e: Default::default(), } } } impl Parser for Trace where P: Parser, I: Stream, D: std::fmt::Display, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { 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(crate) struct Depth { depth: usize, inc: bool, } impl Depth { pub(crate) fn new() -> Self { let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst); let inc = true; Self { depth, inc } } pub(crate) 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 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(crate) enum Severity { Success, Backtrack, Cut, Incomplete, } impl Severity { pub(crate) fn with_result(result: &Result>) -> Self { match result { Ok(_) => Self::Success, Err(ErrMode::Backtrack(_)) => Self::Backtrack, Err(ErrMode::Cut(_)) => Self::Cut, Err(ErrMode::Incomplete(_)) => Self::Incomplete, } } } pub(crate) fn start( 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(crate) 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(crate) fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) { let gutter_style = anstyle::Style::new().bold(); let (call_width, _) = column_widths(); 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 { 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 { std::env::var("COLUMNS") .ok() .and_then(|c| c.parse::().ok()) } winnow-0.6.26/src/combinator/debug/mod.rs000064400000000000000000000060431046102023000164020ustar 00000000000000#![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; /// Trace the execution of the parser /// /// Note that [`Parser::context`] also provides high level trace information. /// /// See [tutorial][crate::_tutorial::chapter_8] 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::combinator::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( name: impl crate::lib::std::fmt::Display, parser: impl Parser, ) -> impl Parser { #[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( name: impl crate::lib::std::fmt::Display, res: &Result>, ) { #[cfg(feature = "debug")] { let depth = internals::Depth::existing(); let severity = internals::Severity::with_result(res); internals::result(*depth, &name, severity); } } pub(crate) struct DisplayDebug(pub(crate) D); impl crate::lib::std::fmt::Display for DisplayDebug { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { write!(f, "{:?}", self.0) } } #[test] #[cfg(feature = "std")] #[cfg_attr(miri, ignore)] #[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()]); } winnow-0.6.26/src/combinator/impls.rs000064400000000000000000000465531046102023000156730ustar 00000000000000//! Opaque implementations of [`Parser`] use crate::combinator::trace; use crate::combinator::trace_result; use crate::combinator::DisplayDebug; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] use crate::error::FromRecoverableError; use crate::error::{AddContext, ErrMode, ErrorKind, FromExternalError, ParserError}; use crate::lib::std::borrow::Borrow; use crate::lib::std::ops::Range; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] use crate::stream::Recover; use crate::stream::StreamIsPartial; use crate::stream::{Location, Stream}; use crate::*; /// [`Parser`] implementation for [`Parser::by_ref`] pub struct ByRef<'p, P> { pub(crate) p: &'p mut P, } impl Parser for ByRef<'_, P> where P: Parser, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { self.p.parse_next(i) } } /// [`Parser`] implementation for [`Parser::map`] pub struct Map where F: Parser, G: FnMut(O) -> O2, { pub(crate) parser: F, pub(crate) map: G, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for Map where F: Parser, G: FnMut(O) -> O2, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { match self.parser.parse_next(i) { Err(e) => Err(e), Ok(o) => Ok((self.map)(o)), } } } /// [`Parser`] implementation for [`Parser::try_map`] pub struct TryMap where F: Parser, G: FnMut(O) -> Result, I: Stream, E: FromExternalError, { pub(crate) parser: F, pub(crate) map: G, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, pub(crate) e2: core::marker::PhantomData, } impl Parser for TryMap where F: Parser, G: FnMut(O) -> Result, I: Stream, E: FromExternalError, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult { let start = input.checkpoint(); let o = self.parser.parse_next(input)?; let res = (self.map)(o).map_err(|err| { input.reset(&start); ErrMode::from_external_error(input, ErrorKind::Verify, err) }); trace_result("verify", &res); res } } /// [`Parser`] implementation for [`Parser::verify_map`] pub struct VerifyMap where F: Parser, G: FnMut(O) -> Option, I: Stream, E: ParserError, { pub(crate) parser: F, pub(crate) map: G, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for VerifyMap where F: Parser, G: FnMut(O) -> Option, I: Stream, E: ParserError, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult { let start = input.checkpoint(); let o = self.parser.parse_next(input)?; let res = (self.map)(o).ok_or_else(|| { input.reset(&start); ErrMode::from_error_kind(input, ErrorKind::Verify) }); trace_result("verify", &res); res } } /// [`Parser`] implementation for [`Parser::and_then`] pub struct AndThen where F: Parser, G: Parser, O: StreamIsPartial, I: Stream, { pub(crate) outer: F, pub(crate) inner: G, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for AndThen where F: Parser, G: Parser, O: StreamIsPartial, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { let start = i.checkpoint(); let mut o = self.outer.parse_next(i)?; let _ = o.complete(); let o2 = self.inner.parse_next(&mut o).map_err(|err| { i.reset(&start); err })?; Ok(o2) } } /// [`Parser`] implementation for [`Parser::parse_to`] pub struct ParseTo where P: Parser, I: Stream, O: crate::stream::ParseSlice, E: ParserError, { pub(crate) p: P, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for ParseTo where P: Parser, I: Stream, O: crate::stream::ParseSlice, E: ParserError, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { let start = i.checkpoint(); let o = self.p.parse_next(i)?; let res = o.parse_slice().ok_or_else(|| { i.reset(&start); ErrMode::from_error_kind(i, ErrorKind::Verify) }); trace_result("verify", &res); res } } /// [`Parser`] implementation for [`Parser::flat_map`] pub struct FlatMap where F: Parser, G: FnMut(O) -> H, H: Parser, { pub(crate) f: F, pub(crate) g: G, pub(crate) h: core::marker::PhantomData, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for FlatMap where F: Parser, G: FnMut(O) -> H, H: Parser, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { let o = self.f.parse_next(i)?; (self.g)(o).parse_next(i) } } /// [`Parser`] implementation for [`Parser::complete_err`] pub struct CompleteErr { pub(crate) f: F, } impl Parser for CompleteErr where I: Stream, F: Parser, E: ParserError, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult { trace("complete_err", |input: &mut I| { match (self.f).parse_next(input) { Err(ErrMode::Incomplete(_)) => { Err(ErrMode::from_error_kind(input, ErrorKind::Complete)) } rest => rest, } }) .parse_next(input) } } /// [`Parser`] implementation for [`Parser::verify`] pub struct Verify where F: Parser, G: FnMut(&O2) -> bool, I: Stream, O: Borrow, O2: ?Sized, E: ParserError, { pub(crate) parser: F, pub(crate) filter: G, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for Verify where F: Parser, G: FnMut(&O2) -> bool, I: Stream, O: Borrow, O2: ?Sized, E: ParserError, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult { let start = input.checkpoint(); let o = self.parser.parse_next(input)?; let res = (self.filter)(o.borrow()).then_some(o).ok_or_else(|| { input.reset(&start); ErrMode::from_error_kind(input, ErrorKind::Verify) }); trace_result("verify", &res); res } } /// [`Parser`] implementation for [`Parser::value`] pub struct Value where F: Parser, O2: Clone, { pub(crate) parser: F, pub(crate) val: O2, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for Value where F: Parser, O2: Clone, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult { (self.parser).parse_next(input).map(|_| self.val.clone()) } } /// [`Parser`] implementation for [`Parser::default_value`] pub struct DefaultValue where F: Parser, O2: core::default::Default, { pub(crate) parser: F, pub(crate) o2: core::marker::PhantomData, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for DefaultValue where F: Parser, O2: core::default::Default, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult { (self.parser).parse_next(input).map(|_| O2::default()) } } /// [`Parser`] implementation for [`Parser::void`] pub struct Void where F: Parser, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for Void where F: Parser, { #[inline(always)] fn parse_next(&mut self, input: &mut I) -> PResult<(), E> { (self.parser).parse_next(input).map(|_| ()) } } /// Replaced with [`Take`] #[deprecated(since = "0.6.14", note = "Replaced with `Take`")] pub type Recognize = Take; /// [`Parser`] implementation for [`Parser::take`] pub struct Take where F: Parser, I: Stream, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser::Slice, E> for Take where F: Parser, I: Stream, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult<::Slice, E> { let checkpoint = input.checkpoint(); match (self.parser).parse_next(input) { Ok(_) => { let offset = input.offset_from(&checkpoint); input.reset(&checkpoint); let taken = input.next_slice(offset); Ok(taken) } Err(e) => Err(e), } } } /// Replaced with [`WithTaken`] #[deprecated(since = "0.6.14", note = "Replaced with `WithTaken`")] pub type WithRecognized = WithTaken; /// [`Parser`] implementation for [`Parser::with_taken`] pub struct WithTaken where F: Parser, I: Stream, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser::Slice), E> for WithTaken where F: Parser, I: Stream, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult<(O, ::Slice), E> { let checkpoint = input.checkpoint(); match (self.parser).parse_next(input) { Ok(result) => { let offset = input.offset_from(&checkpoint); input.reset(&checkpoint); let taken = input.next_slice(offset); Ok((result, taken)) } Err(e) => Err(e), } } } /// [`Parser`] implementation for [`Parser::span`] pub struct Span where F: Parser, I: Stream + Location, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser, E> for Span where F: Parser, I: Stream + Location, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult, E> { let start = input.location(); self.parser.parse_next(input).map(move |_| { let end = input.location(); start..end }) } } /// [`Parser`] implementation for [`Parser::with_span`] pub struct WithSpan where F: Parser, I: Stream + Location, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser), E> for WithSpan where F: Parser, I: Stream + Location, { #[inline] fn parse_next(&mut self, input: &mut I) -> PResult<(O, Range), E> { let start = input.location(); self.parser.parse_next(input).map(move |output| { let end = input.location(); (output, (start..end)) }) } } /// [`Parser`] implementation for [`Parser::output_into`] pub struct OutputInto where F: Parser, O: Into, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) o2: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for OutputInto where F: Parser, O: Into, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { self.parser.parse_next(i).map(|o| o.into()) } } /// [`Parser`] implementation for [`Parser::err_into`] pub struct ErrInto where F: Parser, E: Into, { pub(crate) parser: F, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, pub(crate) e2: core::marker::PhantomData, } impl Parser for ErrInto where F: Parser, E: Into, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { self.parser .parse_next(i) .map_err(|err| err.map(|e| e.into())) } } /// [`Parser`] implementation for [`Parser::context`] pub struct Context where F: Parser, I: Stream, E: AddContext, C: Clone + crate::lib::std::fmt::Debug, { pub(crate) parser: F, pub(crate) context: C, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } impl Parser for Context where F: Parser, I: Stream, E: AddContext, C: Clone + crate::lib::std::fmt::Debug, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { let context = self.context.clone(); trace(DisplayDebug(self.context.clone()), move |i: &mut I| { let start = i.checkpoint(); (self.parser) .parse_next(i) .map_err(|err| err.add_context(i, &start, context.clone())) }) .parse_next(i) } } /// [`Parser`] implementation for [`Parser::retry_after`] #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pub struct RetryAfter where P: Parser, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { pub(crate) parser: P, pub(crate) recover: R, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Parser for RetryAfter where P: Parser, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { if I::is_recovery_supported() { retry_after_inner(&mut self.parser, &mut self.recover, i) } else { self.parser.parse_next(i) } } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] fn retry_after_inner(parser: &mut P, recover: &mut R, i: &mut I) -> PResult where P: Parser, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { loop { let token_start = i.checkpoint(); let mut err = match parser.parse_next(i) { Ok(o) => { return Ok(o); } Err(ErrMode::Incomplete(e)) => return Err(ErrMode::Incomplete(e)), Err(err) => err, }; let err_start = i.checkpoint(); let err_start_eof_offset = i.eof_offset(); if recover.parse_next(i).is_ok() { let i_eof_offset = i.eof_offset(); if err_start_eof_offset == i_eof_offset { // Didn't advance so bubble the error up } else if let Err(err_) = i.record_err(&token_start, &err_start, err) { err = err_; } else { continue; } } i.reset(&err_start); err = err.map(|err| E::from_recoverable_error(&token_start, &err_start, i, err)); return Err(err); } } /// [`Parser`] implementation for [`Parser::resume_after`] #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pub struct ResumeAfter where P: Parser, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { pub(crate) parser: P, pub(crate) recover: R, pub(crate) i: core::marker::PhantomData, pub(crate) o: core::marker::PhantomData, pub(crate) e: core::marker::PhantomData, } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Parser, E> for ResumeAfter where P: Parser, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult, E> { if I::is_recovery_supported() { resume_after_inner(&mut self.parser, &mut self.recover, i) } else { self.parser.parse_next(i).map(Some) } } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] fn resume_after_inner( parser: &mut P, recover: &mut R, i: &mut I, ) -> PResult, E> where P: Parser, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { let token_start = i.checkpoint(); let mut err = match parser.parse_next(i) { Ok(o) => { return Ok(Some(o)); } Err(ErrMode::Incomplete(e)) => return Err(ErrMode::Incomplete(e)), Err(err) => err, }; let err_start = i.checkpoint(); if recover.parse_next(i).is_ok() { if let Err(err_) = i.record_err(&token_start, &err_start, err) { err = err_; } else { return Ok(None); } } i.reset(&err_start); err = err.map(|err| E::from_recoverable_error(&token_start, &err_start, i, err)); Err(err) } winnow-0.6.26/src/combinator/mod.rs000064400000000000000000000340251046102023000153150ustar 00000000000000//! # 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 take a series of tokens for the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer". //! //! | combinator | usage | input | new input | output | comment | //! |---|---|---|---|---|---| //! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` | `"abc"` | `"bc"` | `Ok('a')` |Matches one of the provided [set of tokens][crate::stream::ContainsToken] (works with non ASCII characters too)| //! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` | `"xyab"` | `"yab"` | `Ok('x')` |Matches anything but one of the provided [set of tokens][crate::stream::ContainsToken]| //! | [`literal`][crate::token::literal] | `"hello"` | `"hello world"` | `" world"` | `Ok("hello")` |Recognizes a specific suite of characters or bytes (see also [`Caseless`][crate::ascii::Caseless])| //! | [`take`][crate::token::take] | `take(4)` | `"hello"` | `"o"` | `Ok("hell")` |Takes a specific number of bytes or characters| //! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` | `"abc123"` | `"123"` | `Ok("abc")` |Returns the longest slice of bytes or characters for which the provided [set of tokens][crate::stream::ContainsToken] matches.| //! | [`take_till`][crate::token::take_till] | `take_till(0.., is_alphabetic)` | `"123abc"` | `"abc"` | `Ok("123")` |Returns a slice of bytes or characters until the provided [set of tokens][crate::stream::ContainsToken] matches. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`| //! | [`take_until`][crate::token::take_until] | `take_until(0.., "world")` | `"Hello world"` | `"world"` | `Ok("Hello ")` |Returns a slice of bytes or characters until the provided [literal][crate::token::literal] is found.| //! //! ## Choice combinators //! //! | combinator | usage | input | new input | output | comment | //! |---|---|---|---|---|---| //! | [`alt`] | `alt(("ab", "cd"))` | `"cdef"` | `"ef"` | `Ok("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"` | `"c"` | `Ok(("ab", "cd", "12"))` |Succeeds when all its child parser have succeeded, whatever the order| //! //! ## Sequence combinators //! //! | combinator | usage | input | new input | output | comment | //! |---|---|---|---|---|---| //! | [`(...)` (tuples)][crate::Parser] | `("ab", "XY", take(1))` | `"abXYZ!"` | `"!"` | `Ok(("ab", "XY", "Z"))` |Parse a series of values| //! | [`seq!`] | `seq!(_: '(', take(2), _: ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse a series of values, discarding those you specify| //! | [`delimited`] | `delimited('(', take(2), ')')` | `"(ab)cd"` | `"cd"` | `Ok("ab")` |Parse three values, discarding the first and third value| //! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("XY")` |Parse two values, discarding the first value| //! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `"Z"` | `Ok("ab")` |Parse two values, discarding the second value| //! | [`separated_pair`] | `separated_pair("hello", ',', "world")` | `"hello,world!"` | `"!"` | `Ok(("hello", "world"))` | Parse three values, discarding the middle value| //! //! ## Applying a parser multiple times //! //! | combinator | usage | input | new input | output | comment | //! |---|---|---|---|---|---| //! | [`repeat`] | `repeat(1..=3, "ab")` | `"ababc"` | `"c"` | `Ok(vec!["ab", "ab"])` |Applies the parser between m and n times (n included) and returns the list of results in a Vec| //! | [`repeat_till`] | `repeat_till(0.., "ab", "ef")` | `"ababefg"` | `"g"` | `Ok((vec!["ab", "ab"], "ef"))` |Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second| //! | [`separated`] | `separated(1..=3, "ab", ",")` | `"ab,ab,ab."` | `"."` | `Ok(vec!["ab", "ab", "ab"])` |Applies the parser and separator between m and n times (n included) and returns the list of results in a Vec| //! | [`Repeat::fold`] | repeat(1..=2, `be_u8`).fold(\|\| 0, \|acc, item\| acc + item) | `[1, 2, 3]` | `[3]` | `Ok(3)` |Applies the parser between m and n times (n included) and folds the list of return value| //! //! ## 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::default_value`]: method to replace the result of a parser //! - [`Parser::void`]: method to discard 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::take`]: If the child parser was successful, return the consumed input as the produced value //! - [`Parser::with_taken`]: If the child parser was successful, return a tuple of the consumed input and the produced output. //! - [`Parser::span`]: If the child parser was successful, return the location of the consumed input as the produced value //! - [`Parser::with_span`]: If the child parser was successful, return a tuple of the location of the consumed input and the produced output. //! - [`Parser::verify`]: Returns the result of the child parser if it satisfies a verification function //! //! ## Error management and debugging //! //! - [`cut_err`]: Commit the parse result, disallowing alternative parsers from being attempted //! - [`backtrack_err`]: Attempts 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`]: Print the parse state with the `debug` feature flag //! - [`todo()`]: Placeholder parser //! //! ## Remaining combinators //! //! - [`empty`]: Returns a value without consuming any input, always succeeds //! - [`fail`]: Inversion of [`empty`]. Always fails. //! - [`Parser::by_ref`]: Allow moving `&mut impl Parser` into other parsers //! //! ## Text parsing //! //! - [`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` //! - [`till_line_ending`][crate::ascii::till_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_int]: 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 //! //! - [`take_escaped`][crate::ascii::take_escaped]: Recognize the input slice with escaped characters //! - [`escaped_transform`][crate::ascii::escaped_transform]: Parse escaped characters, unescaping them //! //! ### Character test functions //! //! 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_repeat`][crate::binary::length_repeat] Gets a number from the first parser, then applies the second parser that many times //! - [`length_take`][crate::binary::length_take]: Gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice //! - [`length_and_then`][crate::binary::length_and_then]: Gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns `Incomplete`, `length_value` will return an error //! //! ### Integers //! //! 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 bits //! - [`pattern`][crate::binary::bits::pattern]: Check if a set number of bits matches a pattern //! - [`bool`][crate::binary::bits::bool]: Match any one bit mod branch; mod core; mod debug; mod multi; mod sequence; #[cfg(test)] mod tests; pub mod impls; pub use self::branch::*; pub use self::core::*; pub use self::debug::*; #[deprecated(since = "0.6.23", note = "Replaced with `combinator::impls`")] pub use self::impls::*; pub use self::multi::*; pub use self::sequence::*; #[allow(unused_imports)] use crate::Parser; winnow-0.6.26/src/combinator/multi.rs000064400000000000000000001407011046102023000156670ustar 00000000000000//! Combinators applying their child parser multiple times use crate::combinator::trace; use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::FromExternalError; use crate::error::ParserError; use crate::stream::Accumulate; use crate::stream::Range; use crate::stream::Stream; 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]. /// /// To take a series of tokens, [`Accumulate`] into a `()` /// (e.g. with [`.map(|()| ())`][Parser::map]) /// and then [`Parser::take`]. /// ///
/// /// **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 repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// /// 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 repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// /// 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 repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// /// 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 repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// /// 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( occurrences: impl Into, parser: ParseNext, ) -> Repeat where Input: Stream, Accumulator: Accumulate, ParseNext: Parser, Error: ParserError, { Repeat { occurrences: occurrences.into(), parser, i: Default::default(), o: Default::default(), c: Default::default(), e: Default::default(), } } /// Customizable [`Parser`] implementation for [`repeat`] pub struct Repeat where P: Parser, I: Stream, C: Accumulate, E: ParserError, { occurrences: Range, parser: P, i: core::marker::PhantomData, o: core::marker::PhantomData, c: core::marker::PhantomData, e: core::marker::PhantomData, } impl Repeat where ParseNext: Parser, Input: Stream, Error: ParserError, { /// Repeats the embedded parser, calling `op` to gather the results /// /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Arguments /// * `init` A function returning the initial value. /// * `op` The function that combines a result of `f` with /// the current accumulator. /// ///
/// /// **Warning:** If the parser passed to `fold` accepts empty inputs /// (like `alpha0` or `digit0`), `fold_repeat` will return an error, /// to prevent going into an infinite loop. /// ///
/// /// # Example /// /// Zero or more repetitions: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat( /// 0.., /// "abc" /// ).fold( /// 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::repeat; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat( /// 1.., /// "abc", /// ).fold( /// 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::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` /// /// Arbitrary number of repetitions: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat( /// 0..=2, /// "abc", /// ).fold( /// 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")] #[doc(alias = "fold_repeat")] #[inline(always)] pub fn fold( mut self, mut init: Init, mut op: Op, ) -> impl Parser where Init: FnMut() -> Result, Op: FnMut(Result, Output) -> Result, { let Range { start_inclusive, end_inclusive, } = self.occurrences; trace("repeat_fold", move |i: &mut Input| { match (start_inclusive, end_inclusive) { (0, None) => fold_repeat0_(&mut self.parser, &mut init, &mut op, i), (1, None) => fold_repeat1_(&mut self.parser, &mut init, &mut op, i), (start, end) => fold_repeat_m_n_( start, end.unwrap_or(usize::MAX), &mut self.parser, &mut init, &mut op, i, ), } }) } /// Akin to [`Repeat::fold`], but for containers that can reject an element. /// /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. Additionally, if the fold function returns `None`, the parser will /// stop and return an error. /// /// # Arguments /// * `init` A function returning the initial value. /// * `op` The function that combines a result of `f` with /// the current accumulator. /// ///
/// /// **Warning:** If the parser passed to `repeat` accepts empty inputs /// (like `alpha0` or `digit0`), `verify_fold` will return an error, /// to prevent going into an infinite loop. /// ///
/// /// # Example /// /// Guaranteeing that the input had unique elements: /// ```rust /// # use winnow::error::IResult; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// use std::collections::HashSet; /// /// fn parser(s: &str) -> IResult<&str, HashSet<&str>> { /// repeat( /// 0.., /// "abc" /// ).verify_fold( /// HashSet::new, /// |mut acc: HashSet<_>, item| { /// if acc.insert(item) { /// Some(acc) /// } else { /// None /// } /// } /// ).parse_peek(s) /// } /// /// assert_eq!(parser("abc"), Ok(("", HashSet::from(["abc"])))); /// assert_eq!(parser("abcabc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Verify)))); /// assert_eq!(parser("abc123"), Ok(("123", HashSet::from(["abc"])))); /// assert_eq!(parser("123123"), Ok(("123123", HashSet::from([])))); /// assert_eq!(parser(""), Ok(("", HashSet::from([])))); /// ``` #[inline(always)] pub fn verify_fold( mut self, mut init: Init, mut op: Op, ) -> impl Parser where Init: FnMut() -> Result, Op: FnMut(Result, Output) -> Option, { let Range { start_inclusive, end_inclusive, } = self.occurrences; trace("repeat_verify_fold", move |input: &mut Input| { verify_fold_m_n( start_inclusive, end_inclusive.unwrap_or(usize::MAX), &mut self.parser, &mut init, &mut op, input, ) }) } /// Akin to [`Repeat::fold`], but for containers that can error when an element is accumulated. /// /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. Additionally, if the fold function returns an error, the parser will /// stop and return it. /// /// # Arguments /// * `init` A function returning the initial value. /// * `op` The function that combines a result of `f` with /// the current accumulator. /// ///
/// /// **Warning:** If the parser passed to `repeat` accepts empty inputs /// (like `alpha0` or `digit0`), `try_fold` will return an error, /// to prevent going into an infinite loop. /// ///
/// /// # Example /// /// Writing the output to a vector of bytes: /// ```rust /// # use winnow::error::IResult; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// use std::io::Write; /// use std::io::Error; /// /// fn parser(s: &str) -> IResult<&str, Vec> { /// repeat( /// 0.., /// "abc" /// ).try_fold( /// Vec::new, /// |mut acc, item: &str| -> Result<_, Error> { /// acc.write(item.as_bytes())?; /// Ok(acc) /// } /// ).parse_peek(s) /// } /// /// assert_eq!(parser("abc"), Ok(("", b"abc".to_vec()))); /// assert_eq!(parser("abc123"), Ok(("123", b"abc".to_vec()))); /// assert_eq!(parser("123123"), Ok(("123123", vec![]))); /// assert_eq!(parser(""), Ok(("", vec![]))); #[inline(always)] pub fn try_fold( mut self, mut init: Init, mut op: Op, ) -> impl Parser where Init: FnMut() -> Result, Op: FnMut(Result, Output) -> core::result::Result, Error: FromExternalError, { let Range { start_inclusive, end_inclusive, } = self.occurrences; trace("repeat_try_fold", move |input: &mut Input| { try_fold_m_n( start_inclusive, end_inclusive.unwrap_or(usize::MAX), &mut self.parser, &mut init, &mut op, input, ) }) } } impl Parser for Repeat where P: Parser, I: Stream, C: Accumulate, E: ParserError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { let Range { start_inclusive, end_inclusive, } = self.occurrences; trace("repeat", move |i: &mut I| { match (start_inclusive, end_inclusive) { (0, None) => repeat0_(&mut self.parser, i), (1, None) => repeat1_(&mut self.parser, i), (start, end) if Some(start) == end => repeat_n_(start, &mut self.parser, i), (start, end) => repeat_m_n_(start, end.unwrap_or(usize::MAX), &mut self.parser, i), } }) .parse_next(i) } } fn repeat0_(f: &mut F, i: &mut I) -> PResult where I: Stream, C: Accumulate, F: Parser, E: ParserError, { 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_(f: &mut F, i: &mut I) -> PResult where I: Stream, C: Accumulate, F: Parser, E: ParserError, { let start = i.checkpoint(); match f.parse_next(i) { Err(e) => Err(e.append(i, &start, 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); } } } } } } fn repeat_n_(count: usize, f: &mut F, i: &mut I) -> PResult where I: Stream, C: Accumulate, F: Parser, E: ParserError, { let mut res = C::initial(Some(count)); for _ in 0..count { let start = i.checkpoint(); let len = i.eof_offset(); match f.parse_next(i) { Ok(o) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert(i, "`repeat` parsers must always consume")); } res.accumulate(o); } Err(e) => { return Err(e.append(i, &start, ErrorKind::Many)); } } } Ok(res) } fn repeat_m_n_(min: usize, max: usize, parse: &mut F, input: &mut I) -> PResult where I: Stream, C: Accumulate, F: Parser, E: ParserError, { if min > max { return Err(ErrMode::assert( input, "range should be ascending, rather than descending", )); } 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, &start, ErrorKind::Many))); } else { input.reset(&start); return Ok(res); } } Err(e) => { return Err(e); } } } Ok(res) } /// [`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 take a series of tokens, [`Accumulate`] into a `()` /// (e.g. with [`.map(|((), _)| ())`][Parser::map]) /// and then [`Parser::take`]. /// /// See also /// - [`take_till`][crate::token::take_till] for recognizing up-to a member of a [set of tokens][crate::stream::ContainsToken] /// - [`take_until`][crate::token::take_until] for recognizing up-to a [`literal`][crate::token::literal] (w/ optional simd optimizations) /// /// # Example /// /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat_till; /// /// fn parser(s: &str) -> IResult<&str, (Vec<&str>, &str)> { /// repeat_till(0.., "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_till( occurrences: impl Into, mut parse: ParseNext, mut terminator: TerminatorParser, ) -> impl Parser where Input: Stream, Accumulator: Accumulate, ParseNext: Parser, TerminatorParser: Parser, Error: ParserError, { let Range { start_inclusive, end_inclusive, } = occurrences.into(); trace("repeat_till", move |i: &mut Input| { match (start_inclusive, end_inclusive) { (0, None) => repeat_till0_(&mut parse, &mut terminator, i), (start, end) => repeat_till_m_n_( start, end.unwrap_or(usize::MAX), &mut parse, &mut terminator, i, ), } }) } fn repeat_till0_(f: &mut F, g: &mut G, i: &mut I) -> PResult<(C, P), E> where I: Stream, C: Accumulate, F: Parser, G: Parser, E: ParserError, { 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, &start, 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), } } } fn repeat_till_m_n_( min: usize, max: usize, f: &mut F, g: &mut G, i: &mut I, ) -> PResult<(C, P), E> where I: Stream, C: Accumulate, F: Parser, G: Parser, E: ParserError, { if min > max { return Err(ErrMode::assert( i, "range should be ascending, rather than descending", )); } let mut res = C::initial(Some(min)); let start = i.checkpoint(); for _ in 0..min { match f.parse_next(i) { Ok(o) => { res.accumulate(o); } Err(e) => { return Err(e.append(i, &start, ErrorKind::Many)); } } } for count in min..=max { let start = i.checkpoint(); let len = i.eof_offset(); match g.parse_next(i) { Ok(o) => return Ok((res, o)), Err(ErrMode::Backtrack(err)) => { if count == max { return Err(ErrMode::Backtrack(err)); } i.reset(&start); match f.parse_next(i) { Err(e) => { return Err(e.append(i, &start, ErrorKind::Many)); } Ok(o) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert(i, "`repeat` parsers must always consume")); } res.accumulate(o); } } } Err(e) => return Err(e), } } unreachable!() } /// [`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]. /// /// To take a series of tokens, [`Accumulate`] into a `()` /// (e.g. with [`.map(|()| ())`][Parser::map]) /// and then [`Parser::take`]. /// ///
/// /// **Warning:** If the separator parser accepts empty inputs /// (like `alpha0` or `digit0`), `separated` will return an error, /// to prevent going into an infinite loop. /// ///
/// /// # Example /// /// Zero or more repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// separated(0.., "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![]))); /// # } /// ``` /// /// One or more repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// separated(1.., "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)))); /// # } /// ``` /// /// Fixed number of repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// separated(2, "abc", "|").parse_peek(s) /// } /// /// assert_eq!(parser("abc|abc|abc"), Ok(("|abc", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123abc"), Err(ErrMode::Backtrack(InputError::new("123abc", ErrorKind::Tag)))); /// assert_eq!(parser("abc|def"), Err(ErrMode::Backtrack(InputError::new("def", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Tag)))); /// # } /// ``` /// /// Arbitrary repetitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// separated(0..=2, "abc", "|").parse_peek(s) /// } /// /// assert_eq!(parser("abc|abc|abc"), Ok(("|abc", vec!["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 = "sep_by1")] #[doc(alias = "separated_list0")] #[doc(alias = "separated_list1")] #[doc(alias = "separated_m_n")] #[inline(always)] pub fn separated( occurrences: impl Into, mut parser: ParseNext, mut separator: SepParser, ) -> impl Parser where Input: Stream, Accumulator: Accumulate, ParseNext: Parser, SepParser: Parser, Error: ParserError, { let Range { start_inclusive, end_inclusive, } = occurrences.into(); trace("separated", move |input: &mut Input| { match (start_inclusive, end_inclusive) { (0, None) => separated0_(&mut parser, &mut separator, input), (1, None) => separated1_(&mut parser, &mut separator, input), (start, end) if Some(start) == end => { separated_n_(start, &mut parser, &mut separator, input) } (start, end) => separated_m_n_( start, end.unwrap_or(usize::MAX), &mut parser, &mut separator, input, ), } }) } fn separated0_( parser: &mut P, separator: &mut S, input: &mut I, ) -> PResult where I: Stream, C: Accumulate, P: Parser, S: Parser, E: ParserError, { let mut acc = C::initial(None); let start = input.checkpoint(); match parser.parse_next(input) { Err(ErrMode::Backtrack(_)) => { input.reset(&start); return Ok(acc); } Err(e) => return Err(e), Ok(o) => { acc.accumulate(o); } } loop { let start = input.checkpoint(); let len = input.eof_offset(); match separator.parse_next(input) { Err(ErrMode::Backtrack(_)) => { input.reset(&start); return Ok(acc); } Err(e) => return Err(e), Ok(_) => { // infinite loop check if input.eof_offset() == len { return Err(ErrMode::assert( input, "`separated` separator parser must always consume", )); } match parser.parse_next(input) { Err(ErrMode::Backtrack(_)) => { input.reset(&start); return Ok(acc); } Err(e) => return Err(e), Ok(o) => { acc.accumulate(o); } } } } } } fn separated1_( parser: &mut P, separator: &mut S, input: &mut I, ) -> PResult where I: Stream, C: Accumulate, P: Parser, S: Parser, E: ParserError, { let mut acc = C::initial(None); // Parse the first element match parser.parse_next(input) { Err(e) => return Err(e), Ok(o) => { acc.accumulate(o); } } loop { let start = input.checkpoint(); let len = input.eof_offset(); match separator.parse_next(input) { Err(ErrMode::Backtrack(_)) => { input.reset(&start); return Ok(acc); } Err(e) => return Err(e), Ok(_) => { // infinite loop check if input.eof_offset() == len { return Err(ErrMode::assert( input, "`separated` separator parser must always consume", )); } match parser.parse_next(input) { Err(ErrMode::Backtrack(_)) => { input.reset(&start); return Ok(acc); } Err(e) => return Err(e), Ok(o) => { acc.accumulate(o); } } } } } } fn separated_n_( count: usize, parser: &mut P, separator: &mut S, input: &mut I, ) -> PResult where I: Stream, C: Accumulate, P: Parser, S: Parser, E: ParserError, { let mut acc = C::initial(Some(count)); if count == 0 { return Ok(acc); } let start = input.checkpoint(); match parser.parse_next(input) { Err(e) => { return Err(e.append(input, &start, ErrorKind::Many)); } Ok(o) => { acc.accumulate(o); } } for _ in 1..count { let start = input.checkpoint(); let len = input.eof_offset(); match separator.parse_next(input) { Err(e) => { return Err(e.append(input, &start, ErrorKind::Many)); } Ok(_) => { // infinite loop check if input.eof_offset() == len { return Err(ErrMode::assert( input, "`separated` separator parser must always consume", )); } match parser.parse_next(input) { Err(e) => { return Err(e.append(input, &start, ErrorKind::Many)); } Ok(o) => { acc.accumulate(o); } } } } } Ok(acc) } fn separated_m_n_( min: usize, max: usize, parser: &mut P, separator: &mut S, input: &mut I, ) -> PResult where I: Stream, C: Accumulate, P: Parser, S: Parser, E: ParserError, { if min > max { return Err(ErrMode::assert( input, "range should be ascending, rather than descending", )); } let mut acc = C::initial(Some(min)); let start = input.checkpoint(); match parser.parse_next(input) { Err(ErrMode::Backtrack(e)) => { if min == 0 { input.reset(&start); return Ok(acc); } else { return Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Many))); } } Err(e) => return Err(e), Ok(o) => { acc.accumulate(o); } } for index in 1..max { let start = input.checkpoint(); let len = input.eof_offset(); match separator.parse_next(input) { Err(ErrMode::Backtrack(e)) => { if index < min { return Err(ErrMode::Backtrack(e.append(input, &start, ErrorKind::Many))); } else { input.reset(&start); return Ok(acc); } } Err(e) => { return Err(e); } Ok(_) => { // infinite loop check if input.eof_offset() == len { return Err(ErrMode::assert( input, "`separated` separator parser must always consume", )); } match parser.parse_next(input) { Err(ErrMode::Backtrack(e)) => { if index < min { return Err(ErrMode::Backtrack(e.append( input, &start, ErrorKind::Many, ))); } else { input.reset(&start); return Ok(acc); } } Err(e) => { return Err(e); } Ok(o) => { acc.accumulate(o); } } } } } Ok(acc) } /// 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::Token)))); /// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Verify)))); /// ``` pub fn separated_foldl1( mut parser: ParseNext, mut sep: SepParser, mut op: Op, ) -> impl Parser where Input: Stream, ParseNext: Parser, SepParser: Parser, Error: ParserError, Op: FnMut(Output, Sep, Output) -> Output, { trace("separated_foldl1", move |i: &mut Input| { 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::Token)))); /// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Verify)))); /// ``` #[cfg(feature = "alloc")] pub fn separated_foldr1( mut parser: ParseNext, mut sep: SepParser, mut op: Op, ) -> impl Parser where Input: Stream, ParseNext: Parser, SepParser: Parser, Error: ParserError, Op: FnMut(Output, Sep, Output) -> Output, { trace("separated_foldr1", move |i: &mut Input| { let ol = parser.parse_next(i)?; let all: crate::lib::std::vec::Vec<(Sep, Output)> = repeat(0.., (sep.by_ref(), parser.by_ref())).parse_next(i)?; if let Some((s, or)) = all .into_iter() .rev() .reduce(|(sr, or), (sl, ol)| (sl, op(ol, sr, or))) { let merged = op(ol, s, or); Ok(merged) } else { Ok(ol) } }) } /// Repeats the embedded parser, filling the given slice with results. /// /// This parser fails if the input runs out before the given slice is full. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::fill; /// /// 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<'i, Input, Output, Error, ParseNext>( mut parser: ParseNext, buf: &'i mut [Output], ) -> impl Parser + 'i where Input: Stream + 'i, ParseNext: Parser + 'i, Error: ParserError + 'i, { trace("fill", move |i: &mut Input| { for elem in buf.iter_mut() { let start = i.checkpoint(); match parser.parse_next(i) { Ok(o) => { *elem = o; } Err(e) => { return Err(e.append(i, &start, ErrorKind::Many)); } } } Ok(()) }) } fn fold_repeat0_( f: &mut F, init: &mut H, g: &mut G, input: &mut I, ) -> PResult where I: Stream, F: Parser, G: FnMut(R, O) -> R, H: FnMut() -> R, E: ParserError, { 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_( f: &mut F, init: &mut H, g: &mut G, input: &mut I, ) -> PResult where I: Stream, F: Parser, G: FnMut(R, O) -> R, H: FnMut() -> R, E: ParserError, { let init = init(); let start = input.checkpoint(); match f.parse_next(input) { Err(e) => Err(e.append(input, &start, ErrorKind::Many)), 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_( min: usize, max: usize, parse: &mut F, init: &mut H, fold: &mut G, input: &mut I, ) -> PResult where I: Stream, F: Parser, G: FnMut(R, O) -> R, H: FnMut() -> R, E: ParserError, { if min > max { return Err(ErrMode::assert( input, "range should be ascending, rather than descending", )); } let mut acc = init(); for count in 0..max { let start = input.checkpoint(); let len = input.eof_offset(); match parse.parse_next(input) { Ok(value) => { // infinite loop check: the parser must always consume if input.eof_offset() == len { return Err(ErrMode::assert( input, "`repeat` parsers must always consume", )); } acc = fold(acc, value); } //FInputXMError: handle failure properly Err(ErrMode::Backtrack(err)) => { if count < min { return Err(ErrMode::Backtrack(err.append( input, &start, ErrorKind::Many, ))); } else { input.reset(&start); break; } } Err(e) => return Err(e), } } Ok(acc) } #[inline(always)] fn verify_fold_m_n( min: usize, max: usize, parse: &mut F, init: &mut H, fold: &mut G, input: &mut I, ) -> PResult where I: Stream, F: Parser, G: FnMut(R, O) -> Option, H: FnMut() -> R, E: ParserError, { if min > max { return Err(ErrMode::assert( input, "range should be ascending, rather than descending", )); } let mut acc = init(); for count in 0..max { let start = input.checkpoint(); let len = input.eof_offset(); match parse.parse_next(input) { Ok(value) => { // infinite loop check: the parser must always consume if input.eof_offset() == len { return Err(ErrMode::assert( input, "`repeat` parsers must always consume", )); } let Some(tmp) = fold(acc, value) else { input.reset(&start); let res = Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); super::debug::trace_result("verify_fold", &res); return res; }; acc = tmp; } //FInputXMError: handle failure properly Err(ErrMode::Backtrack(err)) => { if count < min { return Err(ErrMode::Backtrack(err.append( input, &start, ErrorKind::Many, ))); } else { input.reset(&start); break; } } Err(e) => return Err(e), } } Ok(acc) } #[inline(always)] fn try_fold_m_n( min: usize, max: usize, parse: &mut F, init: &mut H, fold: &mut G, input: &mut I, ) -> PResult where I: Stream, F: Parser, G: FnMut(R, O) -> Result, H: FnMut() -> R, E: ParserError + FromExternalError, { if min > max { return Err(ErrMode::assert( input, "range should be ascending, rather than descending", )); } let mut acc = init(); for count in 0..max { let start = input.checkpoint(); let len = input.eof_offset(); match parse.parse_next(input) { Ok(value) => { // infinite loop check: the parser must always consume if input.eof_offset() == len { return Err(ErrMode::assert( input, "`repeat` parsers must always consume", )); } match fold(acc, value) { Ok(tmp) => acc = tmp, Err(e) => { input.reset(&start); let res = Err(ErrMode::from_external_error(input, ErrorKind::Verify, e)); super::debug::trace_result("try_fold", &res); return res; } } } //FInputXMError: handle failure properly Err(ErrMode::Backtrack(err)) => { if count < min { return Err(ErrMode::Backtrack(err.append( input, &start, ErrorKind::Many, ))); } else { input.reset(&start); break; } } Err(e) => return Err(e), } } Ok(acc) } winnow-0.6.26/src/combinator/sequence.rs000064400000000000000000000126341046102023000163500ustar 00000000000000use crate::combinator::trace; use crate::error::ParserError; use crate::stream::Stream; use crate::*; #[doc(inline)] pub use crate::seq; /// Sequence two parsers, only returning the output from the second. /// /// See also [`seq`] to generalize this across any number of fields. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::combinator::preceded; /// /// 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( mut ignored: IgnoredParser, mut parser: ParseNext, ) -> impl Parser where Input: Stream, Error: ParserError, IgnoredParser: Parser, ParseNext: Parser, { trace("preceded", move |input: &mut Input| { let _ = ignored.parse_next(input)?; parser.parse_next(input) }) } /// Sequence two parsers, only returning the output of the first. /// /// See also [`seq`] to generalize this across any number of fields. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::combinator::terminated; /// /// 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( mut parser: ParseNext, mut ignored: IgnoredParser, ) -> impl Parser where Input: Stream, Error: ParserError, ParseNext: Parser, IgnoredParser: Parser, { trace("terminated", move |input: &mut Input| { let o = parser.parse_next(input)?; ignored.parse_next(input).map(|_| o) }) } /// Sequence three parsers, only returning the values of the first and third. /// /// See also [`seq`] to generalize this across any number of fields. /// /// # 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; /// /// 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( mut first: P1, mut sep: SepParser, mut second: P2, ) -> impl Parser where Input: Stream, Error: ParserError, P1: Parser, SepParser: Parser, P2: Parser, { trace("separated_pair", move |input: &mut Input| { let o1 = first.parse_next(input)?; let _ = sep.parse_next(input)?; second.parse_next(input).map(|o2| (o1, o2)) }) } /// Sequence three parsers, only returning the output of the second. /// /// See also [`seq`] to generalize this across any number of fields. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::error::Needed::Size; /// # use winnow::prelude::*; /// use winnow::combinator::delimited; /// /// 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< Input, Ignored1, Output, Ignored2, Error, IgnoredParser1, ParseNext, IgnoredParser2, >( mut ignored1: IgnoredParser1, mut parser: ParseNext, mut ignored2: IgnoredParser2, ) -> impl Parser where Input: Stream, Error: ParserError, IgnoredParser1: Parser, ParseNext: Parser, IgnoredParser2: Parser, { trace("delimited", move |input: &mut Input| { let _ = ignored1.parse_next(input)?; let o2 = parser.parse_next(input)?; ignored2.parse_next(input).map(|_| o2) }) } winnow-0.6.26/src/combinator/tests.rs000064400000000000000000001105631046102023000157020ustar 00000000000000use 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; #[cfg(feature = "alloc")] use crate::lib::std::borrow::ToOwned; 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))); } use crate::lib::std::convert::From; impl From for CustomError { fn from(_: u32) -> Self { CustomError } } impl ParserError for CustomError { fn from_error_kind(_: &I, _: ErrorKind) -> Self { CustomError } fn append(self, _: &I, _: &::Checkpoint, _: 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> = 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, 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_literal(i: Partial<&[u8]>) -> IResult, &[u8]> { peek("abcd").parse_peek(i) } assert_eq!( peek_literal(Partial::new(&b"abcdef"[..])), Ok((Partial::new(&b"abcdef"[..]), &b"abcd"[..])) ); assert_eq!( peek_literal(Partial::new(&b"ab"[..])), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( peek_literal(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, ()> { 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, &[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, (&[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, &[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, &[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, &[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}, }; #[cfg(feature = "alloc")] #[derive(Debug, Clone, Eq, PartialEq)] struct ErrorStr(String); #[cfg(feature = "alloc")] impl From 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 ParserError for ErrorStr { fn from_error_kind(input: &I, kind: ErrorKind) -> Self { ErrorStr(format!("custom error message: ({input:?}, {kind:?})")) } fn append(self, input: &I, _: &::Checkpoint, 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_owned()))) } 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_owned()) ))) ); 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, &[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 alt_dynamic_array() { fn alt1<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8]> { alt(&mut ["a", "bc", "def"][..]).parse_next(i) } let a = &b"a"[..]; assert_eq!(alt1.parse_peek(a), Ok((&b""[..], (&b"a"[..])))); let bc = &b"bc"[..]; assert_eq!(alt1.parse_peek(bc), Ok((&b""[..], (&b"bc"[..])))); let defg = &b"defg"[..]; assert_eq!(alt1.parse_peek(defg), Ok((&b"g"[..], (&b"def"[..])))); } #[test] fn permutation_test() { #[allow(clippy::type_complexity)] fn perm(i: Partial<&[u8]>) -> IResult, (&[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, Vec<&[u8]>> { separated(0.., "abcd", ",").parse_peek(i) } fn multi_empty(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated(0.., "", ",").parse_peek(i) } fn multi_longsep(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated(0.., "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, Vec<&[u8]>> { separated(0.., "abc", "").parse_peek(i) } let i = &b"abcabc"[..]; let i_err_pos = &i[3..]; assert_eq!( empty_sep(Partial::new(i)), Err(ErrMode::Cut(error_position!( &Partial::new(i_err_pos), ErrorKind::Assert ))) ); } #[test] #[cfg(feature = "alloc")] fn separated1_test() { fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated(1.., "abcd", ",").parse_peek(i) } fn multi_longsep(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated(1.., "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 separated_test() { fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated(2..=4, "abcd", ",").parse_peek(i) } let a = &b"abcd,ef"[..]; let b = &b"abcd,abcd,efgh"[..]; let c = &b"abcd,abcd,abcd,abcd,efgh"[..]; let d = &b"abcd,abcd,abcd,abcd,abcd,efgh"[..]; let e = &b"abcd,ab"[..]; 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",abcd,efgh"[..]), res3)) ); assert_eq!( multi(Partial::new(e)), Err(ErrMode::Incomplete(Needed::new(2))) ); } #[test] #[cfg(feature = "alloc")] fn repeat0_test() { fn multi(i: Partial<&[u8]>) -> IResult, 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, Vec<&[u8]>> { repeat(0.., "").parse_peek(i) } assert_eq!( multi_empty(Partial::new(&b"abcdef"[..])), Err(ErrMode::Cut(error_position!( &Partial::new(&b"abcdef"[..]), ErrorKind::Assert ))) ); } #[test] #[cfg(feature = "alloc")] fn repeat1_test() { fn multi(i: Partial<&[u8]>) -> IResult, 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_till(0.., "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 = "alloc")] fn repeat_till_range_test() { #[allow(clippy::type_complexity)] fn multi(i: &str) -> IResult<&str, (Vec<&str>, &str)> { repeat_till(2..=4, "ab", "cd").parse_peek(i) } assert_eq!( multi("cd"), Err(ErrMode::Backtrack(error_node_position!( &"cd", ErrorKind::Many, error_position!(&"cd", ErrorKind::Tag) ))) ); assert_eq!( multi("abcd"), Err(ErrMode::Backtrack(error_node_position!( &"cd", ErrorKind::Many, error_position!(&"cd", ErrorKind::Tag) ))) ); assert_eq!(multi("ababcd"), Ok(("", (vec!["ab", "ab"], "cd")))); assert_eq!(multi("abababcd"), Ok(("", (vec!["ab", "ab", "ab"], "cd")))); assert_eq!( multi("ababababcd"), Ok(("", (vec!["ab", "ab", "ab", "ab"], "cd"))) ); assert_eq!( multi("abababababcd"), Err(ErrMode::Backtrack(error_node_position!( &"cd", ErrorKind::Many, error_position!(&"abcd", 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, 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, 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)] struct NilError; impl From<(I, ErrorKind)> for NilError { fn from(_: (I, ErrorKind)) -> Self { NilError } } impl ParserError for NilError { fn from_error_kind(_: &I, _: ErrorKind) -> NilError { NilError } fn append(self, _: &I, _: &::Checkpoint, _: ErrorKind) -> NilError { NilError } } #[test] #[cfg(feature = "alloc")] fn fold_repeat0_test() { fn fold_into_vec(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { repeat(0.., "abcd") .fold(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(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi_empty(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { repeat(0.., "").fold(Vec::new, fold_into_vec).parse_peek(i) } assert_eq!( multi_empty(Partial::new(&b"abcdef"[..])), Err(ErrMode::Cut(error_position!( &Partial::new(&b"abcdef"[..]), ErrorKind::Assert ))) ); } #[test] #[cfg(feature = "alloc")] fn fold_repeat1_test() { fn fold_into_vec(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { repeat(1.., "abcd") .fold(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::Tag ))) ); assert_eq!( multi(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(2))) ); } #[test] #[cfg(feature = "alloc")] fn fold_repeat_test() { fn fold_into_vec(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { repeat(2..=4, "Abcd") .fold(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 ))) ); } winnow-0.6.26/src/error.rs000064400000000000000000001236011046102023000135310ustar 00000000000000//! # 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: `repeat`, `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: //! - [`EmptyError`] when the reason for failure doesn't matter //! - [`ErrorKind`] //! - [`ContextError`] //! - [`InputError`] (mostly for testing) //! - [`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; /// [Modal error reporting][ErrMode] for [`Parser::parse_next`] /// /// - `Ok(O)` is the parsed value /// - [`Err(ErrMode)`][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 ModalResult = Result>; /// Deprecated, replaced with [`ModalResult`] #[deprecated(since = "0.6.25", note = "Replaced with ModalResult")] pub type PResult = ModalResult; /// Deprecated, replaced with [`PResult`] #[deprecated(since = "0.6.25", note = "Replaced with `ModalResult`")] pub type IResult> = ModalResult<(I, O), 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)] pub enum Needed { /// Needs more data, but we do not know how much Unknown, /// Contains a lower bound on the buffer offset needed to finish parsing /// /// For byte/`&str` streams, this translates to 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 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)] pub enum ErrMode { /// 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] /// combinator. 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 ErrMode { /// 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(self, f: F) -> ErrMode 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(self) -> ErrMode where E: ErrorConvert, { self.map(ErrorConvert::convert) } /// Unwrap the mode, returning the underlying error /// /// Returns `None` for [`ErrMode::Incomplete`] #[inline(always)] pub fn into_inner(self) -> Option { match self { ErrMode::Backtrack(e) | ErrMode::Cut(e) => Some(e), ErrMode::Incomplete(_) => None, } } } impl> ParserError for ErrMode { #[inline(always)] fn from_error_kind(input: &I, kind: ErrorKind) -> Self { ErrMode::Backtrack(E::from_error_kind(input, kind)) } #[inline(always)] fn assert(input: &I, message: &'static str) -> Self where I: crate::lib::std::fmt::Debug, { ErrMode::Cut(E::assert(input, message)) } #[inline] fn append(self, input: &I, token_start: &::Checkpoint, kind: ErrorKind) -> Self { match self { ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, token_start, 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 FromExternalError for ErrMode where E: FromExternalError, { #[inline(always)] fn from_external_error(input: &I, kind: ErrorKind, e: EXT) -> Self { ErrMode::Backtrack(E::from_external_error(input, kind, e)) } } impl> AddContext for ErrMode { #[inline(always)] fn add_context(self, input: &I, token_start: &::Checkpoint, context: C) -> Self { self.map(|err| err.add_context(input, token_start, context)) } } impl ErrMode> { /// Maps `ErrMode>` to `ErrMode>` with the given `F: T -> U` pub fn map_input(self, f: F) -> ErrMode> 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 Eq for ErrMode {} impl fmt::Display for ErrMode where E: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ErrMode::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {u} more data"), ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"), ErrMode::Cut(c) => write!(f, "Parsing Failure: {c:?}"), ErrMode::Backtrack(c) => write!(f, "Parsing Error: {c:?}"), } } } /// 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: Sized { /// Deprecated, replaced with [`ParserError::from_input`] #[deprecated(since = "0.6.26", note = "replaced with `ParserError::from_input`")] fn from_error_kind(input: &I, kind: ErrorKind) -> Self; /// Creates an error from the input position #[inline(always)] fn from_input(input: &I) -> Self { Self::from_error_kind(input, ErrorKind::Fail) } /// Process a parser assertion #[inline(always)] fn assert(input: &I, _message: &'static str) -> Self where I: crate::lib::std::fmt::Debug, { #[cfg(debug_assertions)] panic!("assert `{_message}` failed at {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, token_start: &::Checkpoint, 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: 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, _token_start: &::Checkpoint, _context: C, ) -> Self { self } } /// Capture context from when an error was recovered #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pub trait FromRecoverableError { /// Capture context from when an error was recovered fn from_recoverable_error( token_start: &::Checkpoint, err_start: &::Checkpoint, input: &I, e: E, ) -> 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 { /// 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 { /// 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 { /// The input stream, pointing to the location where the error occurred pub input: I, /// A rudimentary error kind pub kind: ErrorKind, } impl InputError { /// Creates a new basic error #[inline] #[deprecated(since = "0.6.26", note = "replaced with `InputError::at`")] pub fn new(input: I, kind: ErrorKind) -> Self { Self { input, kind } } /// Creates a new basic error #[inline] pub fn at(input: I) -> Self { Self { input, kind: ErrorKind::Fail, } } /// Translate the input type #[inline] pub fn map_input I2>(self, op: O) -> InputError { InputError { input: op(self.input), kind: self.kind, } } } #[cfg(feature = "alloc")] impl InputError<&I> where ::Owned: Clone, { /// Obtaining ownership pub fn into_owned(self) -> InputError<::Owned> { self.map_input(ToOwned::to_owned) } } impl ParserError for InputError { #[inline] fn from_error_kind(input: &I, kind: ErrorKind) -> Self { Self { input: input.clone(), kind, } } #[inline] fn append( self, _input: &I, _token_start: &::Checkpoint, _kind: ErrorKind, ) -> Self { self } } impl AddContext for InputError {} #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl FromRecoverableError for InputError { #[inline] fn from_recoverable_error( _token_start: &::Checkpoint, _err_start: &::Checkpoint, _input: &I, e: Self, ) -> Self { e } } impl FromExternalError for InputError { /// 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 ErrorConvert> for InputError { #[inline] fn convert(self) -> InputError<(I, usize)> { InputError { input: (self.input, 0), kind: self.kind, } } } impl ErrorConvert> for InputError<(I, usize)> { #[inline] fn convert(self) -> InputError { InputError { input: self.input.0, kind: self.kind, } } } /// The Display implementation allows the `std::error::Error` implementation impl fmt::Display for InputError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} error starting at: {}", self.kind.description(), self.input ) } } #[cfg(feature = "std")] impl std::error::Error for InputError { } /// Track an error occurred without any other [`StrContext`] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct EmptyError; impl ParserError for EmptyError { #[inline(always)] fn from_error_kind(_: &I, _: ErrorKind) -> Self { Self } #[inline] fn append( self, _input: &I, _token_start: &::Checkpoint, _kind: ErrorKind, ) -> Self { Self } } impl AddContext for EmptyError {} #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl FromRecoverableError for EmptyError { #[inline(always)] fn from_recoverable_error( _token_start: &::Checkpoint, _err_start: &::Checkpoint, _input: &I, e: Self, ) -> Self { e } } impl FromExternalError for EmptyError { #[inline(always)] fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self { Self } } impl ErrorConvert for EmptyError { #[inline(always)] fn convert(self) -> EmptyError { self } } impl crate::lib::std::fmt::Display for EmptyError { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { "failed to parse".fmt(f) } } impl ParserError for () { #[inline] fn from_error_kind(_: &I, _: ErrorKind) -> Self {} #[inline] fn append( self, _input: &I, _token_start: &::Checkpoint, _kind: ErrorKind, ) -> Self { } } impl AddContext for () {} #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl FromRecoverableError for () { #[inline] fn from_recoverable_error( _token_start: &::Checkpoint, _err_start: &::Checkpoint, _input: &I, (): Self, ) -> Self { } } impl FromExternalError 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 { #[cfg(feature = "alloc")] context: crate::lib::std::vec::Vec, #[cfg(not(feature = "alloc"))] context: core::marker::PhantomData, #[cfg(feature = "std")] cause: Option>, } impl ContextError { /// 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 { 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 Clone for ContextError { fn clone(&self) -> Self { Self { context: self.context.clone(), #[cfg(feature = "std")] cause: self.cause.as_ref().map(|e| e.to_string().into()), } } } impl Default for ContextError { #[inline] fn default() -> Self { Self::new() } } impl ParserError for ContextError { #[inline] fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self { Self::new() } #[inline] fn append( self, _input: &I, _token_start: &::Checkpoint, _kind: ErrorKind, ) -> Self { self } #[inline] fn or(self, other: Self) -> Self { other } } impl AddContext for ContextError { #[inline] fn add_context( mut self, _input: &I, _token_start: &::Checkpoint, context: C, ) -> Self { #[cfg(feature = "alloc")] self.context.push(context); self } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl FromRecoverableError for ContextError { #[inline] fn from_recoverable_error( _token_start: &::Checkpoint, _err_start: &::Checkpoint, _input: &I, e: Self, ) -> Self { e } } #[cfg(feature = "std")] impl FromExternalError for ContextError { #[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 FromExternalError for ContextError { #[inline] fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self { let err = Self::new(); err } } // For tests impl core::cmp::PartialEq for ContextError { 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 { 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::>(); 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(()) } } impl ErrorConvert> for ContextError { #[inline] fn convert(self) -> ContextError { self } } /// 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 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 { /// Initial error that kicked things off Base(TreeErrorBase), /// Traces added to the error while walking back up the stack Stack { /// Initial error that kicked things off base: Box, /// Traces added to the error while walking back up the stack stack: Vec>, }, /// All failed branches of an `alt` Alt(Vec), } /// See [`TreeError::Stack`] #[derive(Debug)] #[cfg(feature = "std")] pub enum TreeErrorFrame { /// See [`ParserError::append`] Kind(TreeErrorBase), /// See [`AddContext::add_context`] Context(TreeErrorContext), } /// See [`TreeErrorFrame::Kind`], [`ParserError::append`] #[derive(Debug)] #[cfg(feature = "std")] pub struct TreeErrorBase { /// 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>, } /// See [`TreeErrorFrame::Context`], [`AddContext::add_context`] #[derive(Debug)] #[cfg(feature = "std")] pub struct TreeErrorContext { /// 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 I: Stream + Clone, ::Owned: Clone, { /// Obtaining ownership pub fn into_owned(self) -> TreeError<::Owned, C> { self.map_input(ToOwned::to_owned) } } #[cfg(feature = "std")] impl TreeError where I: Stream + Clone, { /// Translate the input type pub fn map_input I2>(self, op: O) -> TreeError { 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) -> 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 ParserError for TreeError where I: Stream + Clone, { fn from_error_kind(input: &I, kind: ErrorKind) -> Self { TreeError::Base(TreeErrorBase { input: input.clone(), kind, cause: None, }) } fn append(self, input: &I, token_start: &::Checkpoint, kind: ErrorKind) -> Self { let mut input = input.clone(); input.reset(token_start); let frame = TreeErrorFrame::Kind(TreeErrorBase { input, 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 AddContext for TreeError where I: Stream + Clone, { fn add_context(self, input: &I, token_start: &::Checkpoint, context: C) -> Self { let mut input = input.clone(); input.reset(token_start); let frame = TreeErrorFrame::Context(TreeErrorContext { input, context }); self.append_frame(frame) } } #[cfg(feature = "std")] #[cfg(feature = "unstable-recover")] impl FromRecoverableError for TreeError { #[inline] fn from_recoverable_error( _token_start: &::Checkpoint, _err_start: &::Checkpoint, _input: &I, e: Self, ) -> Self { e } } #[cfg(feature = "std")] impl FromExternalError for TreeError where I: Stream + 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 TreeError where I: Stream + 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 fmt::Display for TreeErrorBase { 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 fmt::Display for TreeErrorContext { 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: Stream + Clone + fmt::Debug + fmt::Display + Sync + Send + 'static, C: fmt::Display + fmt::Debug, > std::error::Error for TreeError { } #[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 fmt::Display for TreeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.write(f, 0) } } /// Deprecated /// /// For en error type, consider [`EmptyError`] instead /// /// For creating an error, use [`ParserError::from_input`], [`InputError::at`] #[rustfmt::skip] #[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)] #[allow(missing_docs)] #[deprecated(since = "0.6.26")] 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 ParserError for ErrorKind { #[inline] fn from_error_kind(_input: &I, kind: ErrorKind) -> Self { kind } #[inline] fn append( self, _input: &I, _token_start: &::Checkpoint, _kind: ErrorKind, ) -> Self { self } } impl AddContext for ErrorKind {} impl FromExternalError 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 { input: I, offset: usize, inner: E, } impl> ParseError { 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 ParseError { /// 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 core::fmt::Display for ParseError 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 {line_num}, column {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_parse_error { use super::*; #[test] fn single_line() { let mut input = "0xZ123"; let start = input.checkpoint(); let _ = input.next_token().unwrap(); let _ = input.next_token().unwrap(); let inner = InputError::new(input, ErrorKind::Slice); let error = ParseError::new(input, start, inner); let expected = "\ 0xZ123 ^ slice error starting at: Z123"; assert_eq!(error.to_string(), expected); } } #[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) => ({ let start = $input.checkpoint(); $crate::error::ParserError::append($next, $input, &start, $code) }); ); winnow-0.6.26/src/lib.rs000064400000000000000000000111741046102023000131470ustar 00000000000000//! > 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) //! - [CHANGELOG](https://github.com/winnow-rs/winnow/blob/v0.6.26/CHANGELOG.md) (includes major version migration //! guides) //! //! ## 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)] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] #[cfg(feature = "alloc")] #[cfg_attr(test, macro_use)] #[allow(unused_extern_crates)] extern crate alloc; #[cfg(doctest)] extern crate doc_comment; #[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 { #![allow(unused_imports)] /// `std` facade allowing `std`/`core` to be interchangeable. Reexports `alloc` crate optionally, /// as well as `core` or `std` #[cfg(not(feature = "std"))] /// internal std exports for no_std compatibility pub(crate) mod std { #[doc(hidden)] #[cfg(not(feature = "alloc"))] pub(crate) use core::borrow; #[cfg(feature = "alloc")] #[doc(hidden)] pub(crate) use alloc::{borrow, boxed, collections, string, vec}; #[doc(hidden)] pub(crate) use core::{ cmp, convert, fmt, hash, iter, mem, ops, option, result, slice, str, }; } #[cfg(feature = "std")] /// internal std exports for `no_std` compatibility pub(crate) mod std { #![allow(clippy::std_instead_of_core)] #[doc(hidden)] pub(crate) use std::{ borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, ops, result, slice, str, string, vec, }; } } #[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; #[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 { /// // ... /// # 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::ModalParser; pub use crate::ModalResult; pub use crate::PResult; pub use crate::Parser; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pub use crate::RecoverableParser as _; } pub use error::IResult; pub use error::ModalResult; pub use error::PResult; pub use parser::*; pub use stream::BStr; pub use stream::Bytes; #[allow(deprecated)] pub use stream::Located; pub use stream::LocatingSlice; pub use stream::Partial; pub use stream::Stateful; pub use stream::Str; winnow-0.6.26/src/macros/dispatch.rs000064400000000000000000000034161046102023000154640ustar 00000000000000/// `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::empty; /// # use winnow::combinator::fail; /// /// fn escaped(input: &mut &str) -> PResult { /// preceded('\\', escape_seq_char).parse_next(input) /// } /// /// fn escape_seq_char(input: &mut &str) -> PResult { /// dispatch! {any; /// 'b' => empty.value('\u{8}'), /// 'f' => empty.value('\u{c}'), /// 'n' => empty.value('\n'), /// 'r' => empty.value('\r'), /// 't' => empty.value('\t'), /// '\\' => empty.value('\\'), /// '"' => empty.value('"'), /// _ => 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::combinator::trace("dispatch", move |i: &mut _| { use $crate::Parser; let initial = $match_parser.parse_next(i)?; match initial { $( $pat $(if $pred)? => $expr.parse_next(i), )* } }) } } winnow-0.6.26/src/macros/mod.rs000064400000000000000000000000601046102023000144340ustar 00000000000000mod dispatch; mod seq; #[cfg(test)] mod tests; winnow-0.6.26/src/macros/seq.rs000064400000000000000000000200131046102023000144450ustar 00000000000000/// Initialize a struct or tuple out of a sequences of parsers /// /// Unlike normal struct initialization syntax: /// - `_` fields can exist to run a parser but ignore the result /// - Parse results for a field can later be referenced using the field name /// /// Unlike normal tuple initialization syntax: /// - Struct-style initialization (`{ 0: _, 1: _}`) is not supported /// - `_: ` fields can exist to run a parser but ignore the result /// ///# Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::ascii::{alphanumeric1, dec_uint, space0}; /// # use winnow::combinator::delimited; /// # use winnow::combinator::empty; /// # use winnow::error::ContextError; /// use winnow::combinator::seq; /// /// #[derive(Default, Debug, PartialEq)] /// struct Field { /// namespace: u32, /// name: Vec, /// value: Vec, /// point: (u32, u32), /// metadata: Vec, /// } /// /// // Parse into structs / tuple-structs /// fn field(input: &mut &[u8]) -> PResult { /// seq!{Field { /// namespace: empty.value(5), /// name: alphanumeric1.map(|s: &[u8]| s.to_owned()), /// // `_` fields are ignored when building the struct /// _: (space0, b':', space0), /// value: alphanumeric1.map(|s: &[u8]| s.to_owned()), /// _: (space0, b':', space0), /// point: point, /// // default initialization also works /// ..Default::default() /// }}.parse_next(input) /// } /// /// // Or parse into tuples /// fn point(input: &mut &[u8]) -> PResult<(u32, u32)> { /// let num = dec_uint::<_, u32, ContextError>; /// seq!(num, _: (space0, b',', space0), num).parse_next(input) /// } /// /// assert_eq!( /// field.parse_peek(&b"test: data: 123 , 4"[..]), /// Ok(( /// &b""[..], /// Field { /// namespace: 5, /// name: b"test"[..].to_owned(), /// value: b"data"[..].to_owned(), /// point: (123, 4), /// metadata: Default::default(), /// }, /// )), /// ); /// ``` #[macro_export] #[doc(alias = "tuple")] #[doc(alias = "preceded")] #[doc(alias = "terminated")] #[doc(alias = "delimited")] #[doc(alias = "pair")] #[doc(alias = "separated_pair")] #[doc(alias = "struct_parser")] #[doc(hidden)] // forced to be visible in intended location macro_rules! seq { ($($name: ident)::* { $($fields: tt)* }) => { $crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| { $crate::seq_parse_struct_fields!(input; $($fields)*); #[allow(clippy::redundant_field_names)] Ok($crate::seq_init_struct_fields!( ($($fields)*); $($name)::*;)) }) }; ($($name: ident)::* ( $($elements: tt)* )) => { $crate::combinator::trace(stringify!($($name)::*), move |input: &mut _| { $crate::seq_parse_tuple_fields!( ($($elements)*) ; ).map(|t| { $crate::seq_init_tuple_fields!( ($($elements)*); (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, t.9, t.10, t.11, t.12, t.13, t.14, t.15, t.16, t.17, t.18, t.19, t.20); $($name)::*; ) }).parse_next(input) }) }; (( $($elements: tt)* )) => { $crate::combinator::trace("tuple", move |input: &mut _| { use $crate::Parser; $crate::seq_parse_tuple_fields!( ($($elements)*) ; ).map(|t| { $crate::seq_init_tuple_fields!( ($($elements)*); (t.0, t.1, t.2, t.3, t.4, t.5, t.6, t.7, t.8, t.9, t.10, t.11, t.12, t.13, t.14, t.15, t.16, t.17, t.18, t.19, t.20); ; ) }).parse_next(input) }) }; ($($elements: tt)*) => { $crate::seq!(($($elements)*)) }; } #[macro_export] #[doc(hidden)] macro_rules! seq_parse_struct_fields { ( $input: ident; _ : $head_parser: expr, $($fields: tt)* ) => { let _ = $crate::Parser::parse_next(&mut $head_parser, $input)?; $crate::seq_parse_struct_fields!($input; $($fields)*) }; ( $input: ident; _ : $head_parser: expr ) => { let _ = $crate::Parser::parse_next(&mut $head_parser, $input)?; }; ( $input: ident; $head_field: ident : $head_parser: expr, $($fields: tt)* ) => { let $head_field = $crate::Parser::parse_next(&mut $head_parser, $input)?; $crate::seq_parse_struct_fields!($input; $($fields)*) }; ( $input: ident; $head_field: ident : $head_parser: expr ) => { let $head_field = $crate::Parser::parse_next(&mut $head_parser, $input)?; }; ( $input: expr; .. $update: expr ) => {}; ( $input: expr; $(,)? ) => {}; } #[macro_export] #[doc(hidden)] macro_rules! seq_parse_tuple_fields { ( (_ : $head_parser: expr, $($fields: tt)* ); $($sequenced: tt)* ) => { $crate::seq_parse_tuple_fields!( ( $($fields)* ) ; $($sequenced)* $head_parser.void(), ) }; ( (_ : $head_parser: expr); $($sequenced: tt)* ) => { $crate::seq_parse_tuple_fields!((); $($sequenced)* $head_parser.void(), ) }; ( ($head_parser: expr, $($fields: tt)*); $($sequenced: tt)* ) => { $crate::seq_parse_tuple_fields!( ( $($fields)* ) ; $($sequenced)* $head_parser, ) }; ( ($head_parser: expr); $($sequenced: tt)* )=> { $crate::seq_parse_tuple_fields!((); $($sequenced)* $head_parser, ) }; ( (); $($sequenced: tt)* ) => { ($($sequenced)*) }; } #[macro_export] #[doc(hidden)] macro_rules! seq_init_struct_fields { ( (_ : $head_parser: expr, $($fields: tt)*); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_struct_fields!( ( $($fields)* ) ; $($name)::* ; $($inits)* ) }; ( (_ : $head_parser: expr); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_struct_fields!( (); $($name)::* ; $($inits)* ) }; ( ($head_field: ident : $head_parser: expr, $($fields: tt)*); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_struct_fields!( ( $($fields)* ) ; $($name)::* ; $($inits)* $head_field: $head_field, ) }; ( ($head_field: ident : $head_parser: expr); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_struct_fields!( (); $($name)::* ; $($inits)* $head_field: $head_field,) }; ( (.. $update: expr); $($name: ident)::*; $($inits: tt)* ) => { $($name)::* { $($inits)* ..$update } }; ( ($(,)?); $($name: ident)::*; $($inits: tt)* ) => { $($name)::* { $($inits)* } }; } #[macro_export] #[doc(hidden)] macro_rules! seq_init_tuple_fields { ( (_ : $head_parser: expr, $($fields: tt)*); ($head_arg: expr, $($args: expr),*); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_tuple_fields!( ( $($fields)* ); ( $($args),* ) ; $($name)::* ; $($inits)* ) }; ( (_ : $head_parser: expr); ($head_arg: expr, $($args: expr),*); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_tuple_fields!((); ( $($args),* ); $($name)::* ; $($inits)*) }; ( ($head_parser: expr, $($fields: tt)*); ($head_arg: expr, $($args: expr),*); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_tuple_fields!( ( $($fields)* ) ; ( $($args),* ) ; $($name)::* ; $($inits)* $head_arg, ) }; ( ($head_parser: expr); ($head_arg: expr, $($args: expr),*); $($name: ident)::*; $($inits: tt)* ) => { $crate::seq_init_tuple_fields!((); ( $($args),* ); $($name)::* ; $($inits)* $head_arg) }; ( (); ($($args: expr),*); $($name: ident)::*; $($inits: expr),* $(,)? ) => { $($name)::*( $($inits,)* ) }; } winnow-0.6.26/src/macros/tests.rs000064400000000000000000000212171046102023000150260ustar 00000000000000use crate::ascii::dec_uint; use crate::combinator::dispatch; use crate::combinator::empty; use crate::combinator::fail; use crate::combinator::seq; use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::ParserError; use crate::prelude::*; use crate::token::any; #[test] fn dispatch_basics() { fn escape_seq_char(input: &mut &str) -> PResult { dispatch! {any; 'b' => empty.value('\u{8}'), 'f' => empty.value('\u{c}'), 'n' => empty.value('\n'), 'r' => empty.value('\r'), 't' => empty.value('\t'), '\\' => empty.value('\\'), '"' => empty.value('"'), _ => fail::<_, char, _>, } .parse_next(input) } assert_eq!(escape_seq_char.parse_peek("b123"), Ok(("123", '\u{8}'))); assert_eq!( escape_seq_char.parse_peek("error"), Err(ErrMode::Backtrack(ParserError::from_error_kind( &"rror", ErrorKind::Fail ))) ); assert_eq!( escape_seq_char.parse_peek(""), Err(ErrMode::Backtrack(ParserError::from_error_kind( &"", ErrorKind::Fail ))) ); } #[test] fn seq_struct_basics() { #[derive(Debug, PartialEq)] struct Point { x: u32, y: u32, } fn parser(input: &mut &str) -> PResult { seq! { Point { x: dec_uint, _: ',', y: dec_uint, } } .parse_next(input) } assert_eq!( parser.parse_peek("123,4 remaining"), Ok((" remaining", Point { x: 123, y: 4 },)), ); assert_eq!( parser.parse_peek("123, remaining"), Err(ErrMode::Backtrack(ParserError::from_error_kind( &" remaining", ErrorKind::Fail ))) ); assert_eq!( parser.parse_peek(""), Err(ErrMode::Backtrack(ParserError::from_error_kind( &"", ErrorKind::Fail ))) ); } #[test] fn seq_struct_default_init() { #[derive(Debug, PartialEq, Default)] struct Point { x: u32, y: u32, z: u32, } fn parser(input: &mut &str) -> PResult { seq! { Point { x: dec_uint, _: ',', y: dec_uint, ..Default::default() } } .parse_next(input) } assert_eq!( parser.parse_peek("123,4 remaining"), Ok((" remaining", Point { x: 123, y: 4, z: 0 },)), ); assert_eq!( parser.parse_peek("123, remaining"), Err(ErrMode::Backtrack(ParserError::from_error_kind( &" remaining", ErrorKind::Fail ))) ); assert_eq!( parser.parse_peek(""), Err(ErrMode::Backtrack(ParserError::from_error_kind( &"", ErrorKind::Fail ))) ); } #[test] fn seq_struct_trailing_comma_elided() { #![allow(dead_code)] #[derive(Debug, PartialEq)] struct Point { x: u32, y: u32, } fn parser(input: &mut &str) -> PResult { seq! { Point { x: dec_uint, _: ',', y: dec_uint, _: empty, } } .parse_next(input) } } #[test] fn seq_struct_no_trailing_comma() { #![allow(dead_code)] #[derive(Debug, PartialEq)] struct Point { x: u32, y: u32, } fn parser(input: &mut &str) -> PResult { seq! { Point { x: dec_uint, _: ',', y: dec_uint } } .parse_next(input) } } #[test] fn seq_struct_no_trailing_comma_elided() { #![allow(dead_code)] #[derive(Debug, PartialEq)] struct Point { x: u32, y: u32, } fn parser(input: &mut &str) -> PResult { seq! { Point { x: dec_uint, _: ',', y: dec_uint, _: empty } } .parse_next(input) } } #[test] fn seq_enum_struct_variant() { #[derive(Debug, PartialEq, Eq)] enum Expr { Add { lhs: u32, rhs: u32 }, Mul(u32, u32), } fn add(input: &mut &[u8]) -> PResult { seq! {Expr::Add { lhs: dec_uint::<_, u32, _>, _: b" + ", rhs: dec_uint::<_, u32, _>, }} .parse_next(input) } fn mul(input: &mut &[u8]) -> PResult { seq!(Expr::Mul( dec_uint::<_, u32, _>, _: b" * ", dec_uint::<_, u32, _>, )) .parse_next(input) } assert_eq!( add.parse_peek(&b"1 + 2"[..]), Ok((&b""[..], Expr::Add { lhs: 1, rhs: 2 })), ); assert_eq!( mul.parse_peek(&b"3 * 4"[..]), Ok((&b""[..], Expr::Mul(3, 4))), ); } #[test] fn seq_tuple_struct_basics() { #[derive(Debug, PartialEq)] struct Point(u32, u32); fn parser(input: &mut &str) -> PResult { seq! { Point( dec_uint, _: ',', dec_uint, ) } .parse_next(input) } assert_eq!( parser.parse_peek("123,4 remaining"), Ok((" remaining", Point(123, 4),)), ); assert_eq!( parser.parse_peek("123, remaining"), Err(ErrMode::Backtrack(ParserError::from_error_kind( &" remaining", ErrorKind::Fail ))) ); assert_eq!( parser.parse_peek(""), Err(ErrMode::Backtrack(ParserError::from_error_kind( &"", ErrorKind::Fail ))) ); } #[test] fn seq_tuple_struct_trailing_comma_elided() { #![allow(dead_code)] #[derive(Debug, PartialEq)] struct Point(u32, u32); fn parser(input: &mut &str) -> PResult { seq! { Point( dec_uint, _: ',', dec_uint, _: empty, ) } .parse_next(input) } } #[test] fn seq_tuple_struct_no_trailing_comma() { #![allow(dead_code)] #[derive(Debug, PartialEq)] struct Point(u32, u32); fn parser(input: &mut &str) -> PResult { seq! { Point( dec_uint, _: ',', dec_uint ) } .parse_next(input) } } #[test] fn seq_tuple_struct_no_trailing_comma_elided() { #![allow(dead_code)] #[derive(Debug, PartialEq)] struct Point(u32, u32); fn parser(input: &mut &str) -> PResult { seq! { Point( dec_uint, _: ',', dec_uint, _: empty ) } .parse_next(input) } } #[test] fn seq_tuple_basics() { fn parser(input: &mut &str) -> PResult<(u32, u32)> { seq! { ( dec_uint, _: ',', dec_uint, ) } .parse_next(input) } assert_eq!( parser.parse_peek("123,4 remaining"), Ok((" remaining", (123, 4),)), ); assert_eq!( parser.parse_peek("123, remaining"), Err(ErrMode::Backtrack(ParserError::from_error_kind( &" remaining", ErrorKind::Fail ))) ); assert_eq!( parser.parse_peek(""), Err(ErrMode::Backtrack(ParserError::from_error_kind( &"", ErrorKind::Fail ))) ); } #[test] fn seq_tuple_trailing_comma_elided() { #![allow(dead_code)] fn parser(input: &mut &str) -> PResult<(u32, u32)> { seq! { ( dec_uint, _: ',', dec_uint, _: empty, ) } .parse_next(input) } } #[test] fn seq_tuple_no_trailing_comma() { #![allow(dead_code)] fn parser(input: &mut &str) -> PResult<(u32, u32)> { seq! { ( dec_uint, _: ',', dec_uint ) } .parse_next(input) } } #[test] fn seq_tuple_no_trailing_comma_elided() { #![allow(dead_code)] fn parser(input: &mut &str) -> PResult<(u32, u32)> { seq! { ( dec_uint, _: ',', dec_uint, _: empty ) } .parse_next(input) } } #[test] fn seq_tuple_no_parens() { #![allow(dead_code)] fn parser(input: &mut &str) -> PResult<(u32, u32)> { seq! ( dec_uint, _: ',', dec_uint, ) .parse_next(input) } } winnow-0.6.26/src/parser.rs000064400000000000000000001321121046102023000136710ustar 00000000000000//! Basic types to build the parsers use crate::ascii::Caseless as AsciiCaseless; use crate::combinator::impls; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] use crate::error::FromRecoverableError; use crate::error::{AddContext, FromExternalError, PResult, ParseError, ParserError}; use crate::stream::{Compare, Location, ParseSlice, Stream, StreamIsPartial}; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] use crate::stream::{Recover, Recoverable}; /// Core trait for parsing /// /// The simplest way to implement a `Parser` is with a function /// ```rust /// use winnow::prelude::*; /// /// fn empty(input: &mut &str) -> PResult<()> { /// let output = (); /// Ok(output) /// } /// /// let (input, output) = empty.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 empty(output: O) -> impl FnMut(&mut &str) -> PResult { /// move |input: &mut &str| { /// let output = output.clone(); /// Ok(output) /// } /// } /// /// let (input, output) = empty("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::literal`][crate::token::literal] pub trait Parser { /// Parse all of `input`, generating `O` from it #[inline] fn parse(&mut self, mut input: I) -> Result> where Self: core::marker::Sized, I: Stream, // Force users to deal with `Incomplete` when `StreamIsPartial` I: StreamIsPartial, E: ParserError, { 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; /// Take tokens from the [`Stream`], turning it into the output /// /// This returns a copy of the [`Stream`] advanced to the next location. /// ///
/// /// Generally, prefer [`Parser::parse_next`]. /// This is primarily intended for: /// - Migrating from older versions / `nom` /// - Testing [`Parser`]s /// /// For look-ahead parsing, see instead [`peek`][crate::combinator::peek]. /// ///
#[inline(always)] fn parse_peek(&mut self, mut input: I) -> PResult<(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_take`][crate::binary::length_take] and `g` into /// [`Parser::complete_err`]: /// ```rust,compile_fail /// # use winnow::prelude::*; /// # use winnow::Parser; /// # use winnow::error::ParserError; /// # use winnow::binary::length_take; /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>( /// mut f: impl Parser<&'i [u8], usize, E>, /// mut g: impl Parser<&'i [u8], O, E> /// ) -> impl Parser<&'i [u8], O, E> { /// move |i: &mut &'i [u8]| { /// let mut data = length_take(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_take; /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>( /// mut f: impl Parser<&'i [u8], usize, E>, /// mut g: impl Parser<&'i [u8], O, E> /// ) -> impl Parser<&'i [u8], O, E> { /// move |i: &mut &'i [u8]| { /// let mut data = length_take(f.by_ref()).parse_next(i)?; /// let o = g.by_ref().complete_err().parse_next(&mut data)?; /// Ok(o) /// } /// } /// ``` #[inline(always)] fn by_ref(&mut self) -> impls::ByRef<'_, Self> where Self: core::marker::Sized, { impls::ByRef { p: 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(self, val: O2) -> impls::Value where Self: core::marker::Sized, O2: Clone, { impls::Value { parser: self, val, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Produce a type's default value /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::alpha1; /// # fn main() { /// /// let mut parser = alpha1.default_value::(); /// /// assert_eq!(parser.parse_peek("abcd"), Ok(("", 0))); /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); /// # } /// ``` #[inline(always)] fn default_value(self) -> impls::DefaultValue where Self: core::marker::Sized, O2: core::default::Default, { impls::DefaultValue { parser: self, o2: Default::default(), i: Default::default(), o: Default::default(), e: Default::default(), } } /// 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) -> impls::Void where Self: core::marker::Sized, { impls::Void { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Convert the parser's output to another type using [`std::convert::From`] /// /// # 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 /// let bytes: IResult<&str, Vec> = parser2.parse_peek("abcd"); /// assert_eq!(bytes, Ok(("", vec![97, 98, 99, 100]))); /// # } /// ``` #[inline(always)] fn output_into(self) -> impls::OutputInto where Self: core::marker::Sized, O: Into, { impls::OutputInto { parser: self, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// 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).take(); /// /// assert_eq!(parser.parse_peek("abcd,efgh"), Ok(("", "abcd,efgh"))); /// assert_eq!(parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Tag)))); /// # } /// ``` #[doc(alias = "concat")] #[doc(alias = "recognize")] #[inline(always)] fn take(self) -> impls::Take where Self: core::marker::Sized, I: Stream, { impls::Take { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Replaced with [`Parser::take`] #[inline(always)] #[deprecated(since = "0.6.14", note = "Replaced with `Parser::take`")] fn recognize(self) -> impls::Take where Self: core::marker::Sized, I: Stream, { impls::Take { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Produce the consumed input with the output /// /// Functions similarly to [take][Parser::take] except it /// returns the parser output as well. /// /// This can be useful especially in cases where the output is not the same type /// 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::literal; /// use winnow::combinator::separated_pair; /// /// fn inner_parser<'s>(input: &mut &'s str) -> PResult> { /// "1234".value(true).parse_next(input) /// } /// /// let mut consumed_parser = separated_pair(alpha1, ',', alpha1).value(true).with_taken(); /// /// assert_eq!(consumed_parser.parse_peek("abcd,efgh1"), Ok(("1", (true, "abcd,efgh")))); /// assert_eq!(consumed_parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Tag)))); /// /// // the second output (representing the consumed input) /// // should be the same as that of the `take` parser. /// let mut take_parser = inner_parser.take(); /// let mut consumed_parser = inner_parser.with_taken().map(|(output, consumed)| consumed); /// /// assert_eq!(take_parser.parse_peek("1234"), consumed_parser.parse_peek("1234")); /// assert_eq!(take_parser.parse_peek("abcd"), consumed_parser.parse_peek("abcd")); /// ``` #[doc(alias = "consumed")] #[doc(alias = "with_recognized")] #[inline(always)] fn with_taken(self) -> impls::WithTaken where Self: core::marker::Sized, I: Stream, { impls::WithTaken { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Replaced with [`Parser::with_taken`] #[inline(always)] #[deprecated(since = "0.6.14", note = "Replaced with `Parser::with_taken`")] fn with_recognized(self) -> impls::WithTaken where Self: core::marker::Sized, I: Stream, { impls::WithTaken { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Produce the location of the consumed input as produced value. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream}; /// use winnow::stream::LocatingSlice; /// use winnow::ascii::alpha1; /// use winnow::combinator::separated_pair; /// /// let mut parser = separated_pair(alpha1.span(), ',', alpha1.span()); /// /// assert_eq!(parser.parse(LocatingSlice::new("abcd,efgh")), Ok((0..4, 5..9))); /// assert_eq!(parser.parse_peek(LocatingSlice::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(LocatingSlice::new("abcd;").peek_slice(4).0, ErrorKind::Tag)))); /// ``` #[inline(always)] fn span(self) -> impls::Span where Self: core::marker::Sized, I: Stream + Location, { impls::Span { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// 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::LocatingSlice; /// use winnow::ascii::alpha1; /// use winnow::token::literal; /// use winnow::combinator::separated_pair; /// /// fn inner_parser<'s>(input: &mut LocatingSlice<&'s str>) -> PResult>> { /// "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(LocatingSlice::new("abcd,efgh")), Ok(((1, 0..4), (2, 5..9)))); /// assert_eq!(consumed_parser.parse_peek(LocatingSlice::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(LocatingSlice::new("abcd;").peek_slice(4).0, ErrorKind::Tag)))); /// /// // the second output (representing the consumed input) /// // should be the same as that of the `span` parser. /// let mut span_parser = inner_parser.span(); /// let mut consumed_parser = inner_parser.with_span().map(|(output, consumed)| consumed); /// /// assert_eq!(span_parser.parse_peek(LocatingSlice::new("1234")), consumed_parser.parse_peek(LocatingSlice::new("1234"))); /// assert_eq!(span_parser.parse_peek(LocatingSlice::new("abcd")), consumed_parser.parse_peek(LocatingSlice::new("abcd"))); /// # } /// ``` #[inline(always)] fn with_span(self) -> impls::WithSpan where Self: core::marker::Sized, I: Stream + Location, { impls::WithSpan { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), } } /// 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(self, map: G) -> impls::Map where G: FnMut(O) -> O2, Self: core::marker::Sized, { impls::Map { parser: self, map, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// Applies a function returning a `Result` over the output of a parser. /// /// # 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::()); /// /// // 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(self, map: G) -> impls::TryMap where Self: core::marker::Sized, G: FnMut(O) -> Result, I: Stream, E: FromExternalError, { impls::TryMap { parser: self, map, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), e2: Default::default(), } } /// Apply both [`Parser::verify`] and [`Parser::map`]. /// /// # 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::().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(self, map: G) -> impls::VerifyMap where Self: core::marker::Sized, G: FnMut(O) -> Option, I: Stream, E: ParserError, { impls::VerifyMap { parser: self, map, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// Creates a parser from the output of this one /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser}; /// use winnow::token::take; /// use winnow::binary::u8; /// /// fn length_take<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// u8.flat_map(take).parse_next(input) /// } /// /// assert_eq!(length_take.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..]))); /// assert_eq!(length_take.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice)))); /// ``` /// /// which is the same as /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser}; /// use winnow::token::take; /// use winnow::binary::u8; /// /// fn length_take<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// let length = u8.parse_next(input)?; /// let data = take(length).parse_next(input)?; /// Ok(data) /// } /// /// assert_eq!(length_take.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..]))); /// assert_eq!(length_take.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice)))); /// ``` #[inline(always)] fn flat_map(self, map: G) -> impls::FlatMap where Self: core::marker::Sized, G: FnMut(O) -> H, H: Parser, { impls::FlatMap { f: self, g: map, h: Default::default(), i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// Applies a second parser over the output of the first one /// /// # 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(self, inner: G) -> impls::AndThen where Self: core::marker::Sized, G: Parser, O: StreamIsPartial, I: Stream, { impls::AndThen { outer: self, inner, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// Apply [`std::str::FromStr`] to the output of the parser /// /// # 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> { /// 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(self) -> impls::ParseTo where Self: core::marker::Sized, I: Stream, O: ParseSlice, E: ParserError, { impls::ParseTo { p: self, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// Returns the output of the child parser if it satisfies a verification function. /// /// 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(self, filter: G) -> impls::Verify where Self: core::marker::Sized, G: FnMut(&O2) -> bool, I: Stream, O: crate::lib::std::borrow::Borrow, O2: ?Sized, E: ParserError, { impls::Verify { parser: self, filter, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } /// If parsing fails, add context to the error /// /// This is used mainly to add user friendly information /// to errors when backtracking through a parse tree. #[doc(alias = "labelled")] #[inline(always)] fn context(self, context: C) -> impls::Context where Self: core::marker::Sized, I: Stream, E: AddContext, C: Clone + crate::lib::std::fmt::Debug, { impls::Context { parser: self, context, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Transforms [`Incomplete`][crate::error::ErrMode::Incomplete] into [`Backtrack`][crate::error::ErrMode::Backtrack] /// /// # 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) -> impls::CompleteErr where Self: core::marker::Sized, { impls::CompleteErr { f: self } } /// Convert the parser's error to another type using [`std::convert::From`] #[inline(always)] fn err_into(self) -> impls::ErrInto where Self: core::marker::Sized, E: Into, { impls::ErrInto { parser: self, i: Default::default(), o: Default::default(), e: Default::default(), e2: Default::default(), } } /// Recover from an error by skipping everything `recover` consumes and trying again /// /// If `recover` consumes nothing, the error is returned, allowing an alternative recovery /// method. /// /// This commits the parse result, preventing alternative branch paths like with /// [`winnow::combinator::alt`][crate::combinator::alt]. #[inline(always)] #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] fn retry_after(self, recover: R) -> impls::RetryAfter where Self: core::marker::Sized, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { impls::RetryAfter { parser: self, recover, i: Default::default(), o: Default::default(), e: Default::default(), } } /// Recover from an error by skipping this parse and everything `recover` consumes /// /// This commits the parse result, preventing alternative branch paths like with /// [`winnow::combinator::alt`][crate::combinator::alt]. #[inline(always)] #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] fn resume_after(self, recover: R) -> impls::ResumeAfter where Self: core::marker::Sized, R: Parser, I: Stream, I: Recover, E: FromRecoverableError, { impls::ResumeAfter { parser: self, recover, i: Default::default(), o: Default::default(), e: Default::default(), } } } impl Parser for F where F: FnMut(&mut I) -> PResult, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { 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> { /// 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::Tag)))); /// assert_eq!(parser.parse_peek(&b"bc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"bc"[..], ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Tag)))); /// ``` impl Parser for u8 where I: StreamIsPartial, I: Stream, I: Compare, E: ParserError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { crate::token::literal(*self).value(*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> { /// '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::Tag)))); /// assert_eq!(parser.parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` impl Parser for char where I: StreamIsPartial, I: Stream, I: Compare, E: ParserError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { crate::token::literal(*self).value(*self).parse_next(i) } } /// This is a shortcut for [`literal`][crate::token::literal]. /// /// # 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> Parser::Slice, E> for &'s [u8] where I: Compare<&'s [u8]> + StreamIsPartial, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult<::Slice, E> { crate::token::literal(*self).parse_next(i) } } /// This is a shortcut for [`literal`][crate::token::literal]. /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::combinator::alt; /// # use winnow::token::take; /// use winnow::ascii::Caseless; /// /// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// alt((Caseless(&"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"hello, World!"[..]), Ok((&b", World!"[..], &b"hello"[..]))); /// 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> Parser::Slice, E> for AsciiCaseless<&'s [u8]> where I: Compare> + StreamIsPartial, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult<::Slice, E> { crate::token::literal(*self).parse_next(i) } } /// This is a shortcut for [`literal`][crate::token::literal]. /// /// # 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, const N: usize> Parser::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<::Slice, E> { crate::token::literal(*self).parse_next(i) } } /// This is a shortcut for [`literal`][crate::token::literal]. /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::combinator::alt; /// # use winnow::token::take; /// use winnow::ascii::Caseless; /// /// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// alt((Caseless(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"hello, World!"[..]), Ok((&b", World!"[..], &b"hello"[..]))); /// 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, const N: usize> Parser::Slice, E> for AsciiCaseless<&'s [u8; N]> where I: Compare> + StreamIsPartial, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult<::Slice, E> { crate::token::literal(*self).parse_next(i) } } /// This is a shortcut for [`literal`][crate::token::literal]. /// /// # 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> Parser::Slice, E> for &'s str where I: Compare<&'s str> + StreamIsPartial, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult<::Slice, E> { crate::token::literal(*self).parse_next(i) } } /// This is a shortcut for [`literal`][crate::token::literal]. /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; /// # use winnow::combinator::alt; /// # use winnow::token::take; /// # use winnow::ascii::Caseless; /// /// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alt((Caseless("hello"), take(5usize))).parse_next(s) /// } /// /// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello"))); /// assert_eq!(parser.parse_peek("hello, World!"), Ok((", World!", "hello"))); /// 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> Parser::Slice, E> for AsciiCaseless<&'s str> where I: Compare> + StreamIsPartial, I: Stream, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult<::Slice, E> { crate::token::literal(*self).parse_next(i) } } impl> Parser 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, $($parser),+> Parser for ($($parser),+,) where $($parser: Parser),+ { #[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),+); } } /// Trait alias for [`Parser`] to ease the transition to the next release pub trait ModalParser: Parser {} impl ModalParser for P where P: Parser {} /// Collect all errors when parsing the input /// /// [`Parser`]s will need to use [`Recoverable`] for their input. #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pub trait RecoverableParser { /// Collect all errors when parsing the input /// /// If `self` fails, this acts like [`Parser::resume_after`] and returns `Ok(None)`. /// Generally, this should be avoided by using /// [`Parser::retry_after`] and [`Parser::resume_after`] throughout your parser. /// /// The empty `input` is returned to allow turning the errors into [`ParserError`]s. fn recoverable_parse(&mut self, input: I) -> (I, Option, Vec); } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl RecoverableParser for P where P: Parser, O, E>, I: Stream, I: StreamIsPartial, R: FromRecoverableError, E>, R: crate::lib::std::fmt::Debug, E: FromRecoverableError, E>, E: ParserError>, E: crate::lib::std::fmt::Debug, { fn recoverable_parse(&mut self, input: I) -> (I, Option, Vec) { debug_assert!( !I::is_partial_supported(), "partial streams need to handle `ErrMode::Incomplete`" ); let start = input.checkpoint(); let mut input = Recoverable::new(input); let start_token = input.checkpoint(); let result = ( self.by_ref(), crate::combinator::eof.resume_after(crate::token::rest.void()), ) .parse_next(&mut input); let (o, err) = match result { Ok((o, _)) => (Some(o), None), Err(err) => { let err = err .into_inner() .expect("complete parsers should not report `ErrMode::Incomplete(_)`"); let err_start = input.checkpoint(); let err = R::from_recoverable_error(&start_token, &err_start, &input, err); (None, Some(err)) } }; let (mut input, mut errs) = input.into_parts(); input.reset(&start); if let Some(err) = err { errs.push(err); } (input, o, errs) } } 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 crate::lib::std::boxed::Box; #[cfg(feature = "alloc")] impl Parser for Box + '_> { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { (**self).parse_next(i) } } /// Deprecated #[inline(always)] #[deprecated(since = "0.6.23")] #[allow(deprecated)] pub fn unpeek<'a, I, O, E>( mut peek: impl FnMut(I) -> crate::error::IResult + 'a, ) -> impl FnMut(&mut I) -> PResult 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::IResult; 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, 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, (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(("", ()))); } } winnow-0.6.26/src/stream/impls.rs000064400000000000000000000322501046102023000150160ustar 00000000000000macro_rules! impl_partial_eq { ($lhs:ty, $rhs:ty) => { #[allow(unused_lifetimes)] impl<'a> 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) } } #[allow(unused_lifetimes)] impl<'a> PartialEq<$lhs> for $rhs { #[inline] fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(other, self) } } }; } macro_rules! impl_partial_ord { ($lhs:ty, $rhs:ty) => { #[allow(unused_lifetimes)] impl<'a> PartialOrd<$rhs> for $lhs { #[inline] fn partial_cmp(&self, other: &$rhs) -> Option { let l = self.as_ref(); let r: &Self = other.as_ref(); PartialOrd::partial_cmp(l, r) } } #[allow(unused_lifetimes)] impl<'a> PartialOrd<$lhs> for $rhs { #[inline] fn partial_cmp(&self, other: &$lhs) -> Option { 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 { ::fmt(self, f) } } impl fmt::Debug for Bytes { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::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, "{byte:0>2x}")?; } 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, "{byte:0>2X}")?; } Ok(()) } } impl ops::Deref for Bytes { type Target = [u8]; #[inline] fn deref(&self) -> &[u8] { self.as_bytes() } } impl ops::Index for Bytes { type Output = u8; #[inline] fn index(&self, idx: usize) -> &u8 { &self.as_bytes()[idx] } } impl ops::Index for Bytes { type Output = Bytes; #[inline] fn index(&self, _: ops::RangeFull) -> &Bytes { self } } impl ops::Index> for Bytes { type Output = Bytes; #[inline] fn index(&self, r: ops::Range) -> &Bytes { Bytes::new(&self.as_bytes()[r.start..r.end]) } } impl ops::Index> for Bytes { type Output = Bytes; #[inline] fn index(&self, r: ops::RangeInclusive) -> &Bytes { Bytes::new(&self.as_bytes()[*r.start()..=*r.end()]) } } impl ops::Index> for Bytes { type Output = Bytes; #[inline] fn index(&self, r: ops::RangeFrom) -> &Bytes { Bytes::new(&self.as_bytes()[r.start..]) } } impl ops::Index> for Bytes { type Output = Bytes; #[inline] fn index(&self, r: ops::RangeTo) -> &Bytes { Bytes::new(&self.as_bytes()[..r.end]) } } impl ops::Index> for Bytes { type Output = Bytes; #[inline] fn index(&self, r: ops::RangeToInclusive) -> &Bytes { Bytes::new(&self.as_bytes()[..=r.end]) } } impl AsRef<[u8]> for Bytes { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl AsRef for [u8] { #[inline] fn as_ref(&self) -> &Bytes { Bytes::new(self) } } impl AsRef 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; #[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 for crate::lib::std::vec::Vec { #[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 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 { 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 let _ = 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"); let _ = format!("{total:#?}"); let _ = format!("{:#?}", &total[1..]); let _ = 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 for BStr { type Output = u8; #[inline] fn index(&self, idx: usize) -> &u8 { &self.as_bytes()[idx] } } impl ops::Index for BStr { type Output = BStr; #[inline] fn index(&self, _: ops::RangeFull) -> &BStr { self } } impl ops::Index> for BStr { type Output = BStr; #[inline] fn index(&self, r: ops::Range) -> &BStr { BStr::new(&self.as_bytes()[r.start..r.end]) } } impl ops::Index> for BStr { type Output = BStr; #[inline] fn index(&self, r: ops::RangeInclusive) -> &BStr { BStr::new(&self.as_bytes()[*r.start()..=*r.end()]) } } impl ops::Index> for BStr { type Output = BStr; #[inline] fn index(&self, r: ops::RangeFrom) -> &BStr { BStr::new(&self.as_bytes()[r.start..]) } } impl ops::Index> for BStr { type Output = BStr; #[inline] fn index(&self, r: ops::RangeTo) -> &BStr { BStr::new(&self.as_bytes()[..r.end]) } } impl ops::Index> for BStr { type Output = BStr; #[inline] fn index(&self, r: ops::RangeToInclusive) -> &BStr { BStr::new(&self.as_bytes()[..=r.end]) } } impl AsRef<[u8]> for BStr { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl AsRef for [u8] { #[inline] fn as_ref(&self) -> &BStr { BStr::new(self) } } impl AsRef 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; #[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 for crate::lib::std::vec::Vec { #[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 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 { 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"); } } } winnow-0.6.26/src/stream/mod.rs000064400000000000000000002773261046102023000144700ustar 00000000000000//! 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 //! - [`LocatingSlice`] can track the location within the original buffer to report //! [spans][crate::Parser::with_span] //! - [`Stateful`] to thread global state through your parsers //! - [`Partial`] can mark an input as partial buffer that is being streamed into //! - [Custom stream types][crate::_topic::stream] use core::hash::BuildHasher; use core::num::NonZeroUsize; use crate::ascii::Caseless as AsciiCaseless; #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] use crate::error::FromRecoverableError; use crate::error::Needed; use crate::lib::std::iter::{Cloned, Enumerate}; 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(any(feature = "unstable-doc", feature = "unstable-recover"))] use crate::error::ErrMode; #[cfg(feature = "alloc")] use crate::lib::std::collections::BTreeMap; #[cfg(feature = "alloc")] use crate::lib::std::collections::BTreeSet; #[cfg(feature = "std")] use crate::lib::std::collections::HashMap; #[cfg(feature = "std")] use crate::lib::std::collections::HashSet; #[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::derived_hash_with_manual_eq)] #[derive(Hash)] #[repr(transparent)] pub struct Bytes([u8]); impl Bytes { /// Make a stream out of a byte slice-like. #[inline] pub fn new>(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::derived_hash_with_manual_eq)] #[derive(Hash)] #[repr(transparent)] pub struct BStr([u8]); impl BStr { /// Make a stream out of a byte slice-like. #[inline] pub fn new>(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 } } /// Deprecated, replaced with [`LocatingSlice`] #[deprecated(since = "0.6.23", note = "Replaced with `LocatingSlice`")] pub type Located = LocatingSlice; /// Allow collecting the span of a parsed token within a slice /// /// Spans are tracked as a [`Range`] 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, PartialEq, Eq, PartialOrd, Ord)] #[doc(alias = "LocatingSliceSpan")] #[doc(alias = "Located")] pub struct LocatingSlice { initial: I, input: I, } impl LocatingSlice where I: Clone + Offset, { /// Wrap another Stream with span tracking pub fn new(input: I) -> Self { let initial = input.clone(); Self { initial, input } } #[inline] fn location(&self) -> usize { self.input.offset_from(&self.initial) } } impl LocatingSlice where I: Clone + Stream + Offset, { /// Reset the stream to the start /// /// This is useful for formats that encode a graph with addresses relative to the start of the /// input. #[doc(alias = "fseek")] #[inline] pub fn reset_to_start(&mut self) { let start = self.initial.checkpoint(); self.input.reset(&start); } } impl AsRef for LocatingSlice { #[inline(always)] fn as_ref(&self) -> &I { &self.input } } impl crate::lib::std::ops::Deref for LocatingSlice { type Target = I; #[inline(always)] fn deref(&self) -> &Self::Target { &self.input } } impl crate::lib::std::fmt::Display for LocatingSlice { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { self.input.fmt(f) } } impl crate::lib::std::fmt::Debug for LocatingSlice { #[inline] fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { self.input.fmt(f) } } /// Allow recovering from parse errors, capturing them as the parser continues /// /// Generally, this will be used indirectly via /// [`RecoverableParser::recoverable_parse`][crate::RecoverableParser::recoverable_parse]. #[cfg(feature = "unstable-recover")] #[derive(Clone)] #[cfg(feature = "std")] pub struct Recoverable where I: Stream, { input: I, errors: Vec, is_recoverable: bool, } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Default for Recoverable where I: Default + Stream, { #[inline] fn default() -> Self { Self::new(I::default()) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recoverable where I: Stream, { /// Track recoverable errors with the stream #[inline] pub fn new(input: I) -> Self { Self { input, errors: Default::default(), is_recoverable: true, } } /// Act as a normal stream #[inline] pub fn unrecoverable(input: I) -> Self { Self { input, errors: Default::default(), is_recoverable: false, } } /// Access the current input and errors #[inline] pub fn into_parts(self) -> (I, Vec) { (self.input, self.errors) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl AsRef for Recoverable where I: Stream, { #[inline(always)] fn as_ref(&self) -> &I { &self.input } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl crate::lib::std::ops::Deref for Recoverable where I: Stream, { type Target = I; #[inline(always)] fn deref(&self) -> &Self::Target { &self.input } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl crate::lib::std::fmt::Display for Recoverable where I: Stream, { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { crate::lib::std::fmt::Display::fmt(&self.input, f) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl crate::lib::std::fmt::Debug for Recoverable { #[inline] fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { if f.alternate() { self.input.fmt(f) } else { f.debug_struct("Recoverable") .field("input", &self.input) .field("errors", &self.errors) .field("is_recoverable", &self.is_recoverable) .finish() } } } /// Thread global state through your parsers /// /// Use cases /// - Recursion checks /// - Error recovery /// - Debugging /// /// # Example /// /// ``` /// # use std::cell::Cell; /// # use winnow::prelude::*; /// # use winnow::stream::Stateful; /// # use winnow::ascii::alpha1; /// # type Error = (); /// /// #[derive(Debug)] /// struct State<'s>(&'s mut u32); /// /// impl<'s> State<'s> { /// fn count(&mut self) { /// *self.0 += 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 mut state = 0; /// let input = Stream { input: data, state: State(&mut state) }; /// let output = word.parse(input).unwrap(); /// assert_eq!(state, 1); /// ``` #[derive(Clone, Copy, Default, Eq, PartialEq)] #[doc(alias = "LocatingSliceSpan")] pub struct Stateful { /// Inner input being wrapped in state pub input: I, /// User-provided state pub state: S, } impl AsRef for Stateful { #[inline(always)] fn as_ref(&self) -> &I { &self.input } } impl crate::lib::std::ops::Deref for Stateful { type Target = I; #[inline(always)] fn deref(&self) -> &Self::Target { self.as_ref() } } impl crate::lib::std::fmt::Display for Stateful { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { self.input.fmt(f) } } impl crate::lib::std::fmt::Debug for Stateful { #[inline] fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { if f.alternate() { self.input.fmt(f) } else { f.debug_struct("Stateful") .field("input", &self.input) .field("state", &self.state) .finish() } } } /// Mark the input as a partial buffer for streaming input. /// /// Complete input means that we already have all of the data. This will be the common case with /// 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>> { /// 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 takes 0 or more alphabetic characters /// fn alpha0_partial<'s>(i: &mut Partial<&'s str>) -> PResult<&'s str, InputError>> { /// 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 taken characters, both parsers work the same way /// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd;")), Ok((Partial::new(";"), "abcd"))); /// assert_eq!(alpha0_complete.parse_peek("abcd;"), Ok((";", "abcd"))); /// /// // but when there's no limit, the partial version returns `Incomplete`, because it cannot /// // know if more input data should be taken. The whole input could be "abcd;", or /// // "abcde;" /// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd")), Err(ErrMode::Incomplete(Needed::new(1)))); /// /// // while the complete version knows that all of the data is there /// assert_eq!(alpha0_complete.parse_peek("abcd"), Ok(("", "abcd"))); /// ``` #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Partial { input: I, partial: bool, } impl Partial where I: StreamIsPartial, { /// Create a partial input #[inline] 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 Default for Partial where I: Default + StreamIsPartial, { #[inline] fn default() -> Self { Self::new(I::default()) } } impl crate::lib::std::ops::Deref for Partial { type Target = I; #[inline(always)] fn deref(&self) -> &Self::Target { &self.input } } impl crate::lib::std::fmt::Display for Partial { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { self.input.fmt(f) } } impl crate::lib::std::fmt::Debug for Partial { #[inline] fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { if f.alternate() { self.input.fmt(f) } else { f.debug_struct("Partial") .field("input", &self.input) .field("partial", &self.partial) .finish() } } } /// Abstract method to calculate the input length pub trait SliceLen { /// Calculates the input length, as indicated by its name, /// and the name of the trait itself fn slice_len(&self) -> usize; } impl SliceLen for AsciiCaseless { #[inline(always)] fn slice_len(&self) -> usize { self.0.slice_len() } } impl SliceLen for &[T] { #[inline(always)] fn slice_len(&self) -> usize { self.len() } } impl SliceLen for [T; LEN] { #[inline(always)] fn slice_len(&self) -> usize { self.len() } } impl SliceLen for &[T; LEN] { #[inline(always)] fn slice_len(&self) -> usize { self.len() } } impl SliceLen for &str { #[inline(always)] fn slice_len(&self) -> usize { self.len() } } impl SliceLen for u8 { #[inline(always)] fn slice_len(&self) -> usize { 1 } } impl SliceLen for char { #[inline(always)] fn slice_len(&self) -> usize { self.len_utf8() } } impl SliceLen for &Bytes { #[inline(always)] fn slice_len(&self) -> usize { self.len() } } impl SliceLen for &BStr { #[inline(always)] fn slice_len(&self) -> usize { self.len() } } impl 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 SliceLen for LocatingSlice where I: SliceLen, { #[inline(always)] fn slice_len(&self) -> usize { self.input.slice_len() } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl SliceLen for Recoverable where I: SliceLen, I: Stream, { #[inline(always)] fn slice_len(&self) -> usize { self.input.slice_len() } } impl SliceLen for Stateful where I: SliceLen, { #[inline(always)] fn slice_len(&self) -> usize { self.input.slice_len() } } impl SliceLen for Partial where I: SliceLen, { #[inline(always)] fn slice_len(&self) -> usize { self.input.slice_len() } } /// Core definition for parser input state pub trait Stream: Offset<::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 `LocatingSlice<&[u8]>` or `&str` for `LocatingSlice<&str>` type Slice: crate::lib::std::fmt::Debug; /// Iterate with the offset from the current location type IterOffsets: Iterator; /// 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; /// 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

(&self, predicate: P) -> Option 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; /// 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>>; type Checkpoint = Checkpoint; #[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 { let (token, next) = self.split_first()?; *self = next; Some(token.clone()) } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.iter().position(|b| predicate(b.clone())) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(*self) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { *self = checkpoint.inner; } #[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; #[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 { let c = self.chars().next()?; let offset = c.len(); *self = &self[offset..]; Some(c) } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option 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 { 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>::new(*self) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { *self = checkpoint.inner; } #[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>>; type Checkpoint = Checkpoint; #[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 { if self.is_empty() { None } else { let token = self[0]; *self = &self[1..]; Some(token) } } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.iter().position(|b| predicate(*b)) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(*self) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { *self = checkpoint.inner; } #[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>>; type Checkpoint = Checkpoint; #[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 { if self.is_empty() { None } else { let token = self[0]; *self = &self[1..]; Some(token) } } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.iter().position(|b| predicate(*b)) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(*self) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { *self = checkpoint.inner; } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { self } } impl Stream for (I, usize) where I: Stream + Clone, { type Token = bool; type Slice = (I::Slice, usize, usize); type IterOffsets = BitOffsets; type Checkpoint = Checkpoint<(I::Checkpoint, usize), Self>; #[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 { next_bit(self) } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option 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 { 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>::new((self.0.checkpoint(), self.1)) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { self.0.reset(&checkpoint.inner.0); self.1 = checkpoint.inner.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, usize), o: usize, } impl Iterator for BitOffsets where I: Stream + Clone, { type Item = (usize, bool); fn next(&mut self) -> Option { let b = next_bit(&mut self.i)?; let o = self.o; self.o += 1; Some((o, b)) } } fn next_bit(i: &mut (I, usize)) -> Option where I: Stream + 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 Stream for LocatingSlice { type Token = ::Token; type Slice = ::Slice; type IterOffsets = ::IterOffsets; type Checkpoint = 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.input.next_token() } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.input.offset_for(predicate) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(self.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { self.input.reset(&checkpoint.inner); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Stream for Recoverable where I: Stream, { type Token = ::Token; type Slice = ::Slice; type IterOffsets = ::IterOffsets; type Checkpoint = 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.input.next_token() } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.input.offset_for(predicate) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(self.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { self.input.reset(&checkpoint.inner); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } impl Stream for Stateful { type Token = ::Token; type Slice = ::Slice; type IterOffsets = ::IterOffsets; type Checkpoint = 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.input.next_token() } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.input.offset_for(predicate) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(self.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { self.input.reset(&checkpoint.inner); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } impl Stream for Partial { type Token = ::Token; type Slice = ::Slice; type IterOffsets = ::IterOffsets; type Checkpoint = 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.input.next_token() } #[inline(always)] fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.input.offset_for(predicate) } #[inline(always)] fn offset_at(&self, tokens: usize) -> Result { 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>::new(self.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: &Self::Checkpoint) { self.input.reset(&checkpoint.inner); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } /// Number of indices input has advanced since start of parsing /// /// See [`LocatingSlice`] for adding location tracking to your [`Stream`] pub trait Location { /// Number of indices input has advanced since start of parsing fn location(&self) -> usize; } impl Location for LocatingSlice where I: Clone + Offset, { #[inline(always)] fn location(&self) -> usize { self.location() } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Location for Recoverable where I: Location, I: Stream, { #[inline(always)] fn location(&self) -> usize { self.input.location() } } impl Location for Stateful where I: Location, { #[inline(always)] fn location(&self) -> usize { self.input.location() } } impl Location for Partial where I: Location, { #[inline(always)] fn location(&self) -> usize { self.input.location() } } /// Capture top-level errors in the middle of parsing so parsing can resume /// /// See [`Recoverable`] for adding error recovery tracking to your [`Stream`] #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] pub trait Recover: Stream { /// Capture a top-level error /// /// May return `Err(err)` if recovery is not possible (e.g. if [`Recover::is_recovery_supported`] /// returns `false`). fn record_err( &mut self, token_start: &Self::Checkpoint, err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode>; /// Report whether the [`Stream`] can save off errors for recovery fn is_recovery_supported() -> bool; } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl<'a, T, E> Recover for &'a [T] where &'a [T]: Stream, { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for &str { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for &Bytes { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for &BStr { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for (I, usize) where I: Recover, I: Stream + Clone, { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for LocatingSlice where I: Recover, I: Stream, { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for Recoverable where I: Stream, R: FromRecoverableError, R: crate::lib::std::fmt::Debug, { fn record_err( &mut self, token_start: &Self::Checkpoint, err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { if self.is_recoverable { match err { ErrMode::Incomplete(need) => Err(ErrMode::Incomplete(need)), ErrMode::Backtrack(err) | ErrMode::Cut(err) => { self.errors .push(R::from_recoverable_error(token_start, err_start, self, err)); Ok(()) } } } else { Err(err) } } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { true } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for Stateful where I: Recover, I: Stream, S: Clone + crate::lib::std::fmt::Debug, { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Recover for Partial where I: Recover, I: Stream, { #[inline(always)] fn record_err( &mut self, _token_start: &Self::Checkpoint, _err_start: &Self::Checkpoint, err: ErrMode, ) -> Result<(), ErrMode> { Err(err) } /// Report whether the [`Stream`] can save off errors for recovery #[inline(always)] fn is_recovery_supported() -> bool { false } } /// 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 StreamIsPartial for &[T] { type PartialState = (); #[inline] fn complete(&mut self) -> Self::PartialState {} #[inline] fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl StreamIsPartial for &str { type PartialState = (); #[inline] fn complete(&mut self) -> Self::PartialState { // Already complete } #[inline] fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl StreamIsPartial for &Bytes { type PartialState = (); #[inline] fn complete(&mut self) -> Self::PartialState { // Already complete } #[inline] fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl StreamIsPartial for &BStr { type PartialState = (); #[inline] fn complete(&mut self) -> Self::PartialState { // Already complete } #[inline] fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl StreamIsPartial for (I, usize) where I: StreamIsPartial, { type PartialState = I::PartialState; #[inline] fn complete(&mut self) -> Self::PartialState { self.0.complete() } #[inline] fn restore_partial(&mut self, state: Self::PartialState) { self.0.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.0.is_partial() } } impl StreamIsPartial for LocatingSlice where I: StreamIsPartial, { type PartialState = I::PartialState; #[inline] fn complete(&mut self) -> Self::PartialState { self.input.complete() } #[inline] fn restore_partial(&mut self, state: Self::PartialState) { self.input.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.input.is_partial() } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl StreamIsPartial for Recoverable where I: StreamIsPartial, I: Stream, { type PartialState = I::PartialState; #[inline] fn complete(&mut self) -> Self::PartialState { self.input.complete() } #[inline] fn restore_partial(&mut self, state: Self::PartialState) { self.input.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.input.is_partial() } } impl StreamIsPartial for Stateful where I: StreamIsPartial, { type PartialState = I::PartialState; #[inline] fn complete(&mut self) -> Self::PartialState { self.input.complete() } #[inline] fn restore_partial(&mut self, state: Self::PartialState) { self.input.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.input.is_partial() } } impl StreamIsPartial for Partial where I: StreamIsPartial, { type PartialState = bool; #[inline] fn complete(&mut self) -> Self::PartialState { core::mem::replace(&mut self.partial, false) } #[inline] fn restore_partial(&mut self, state: Self::PartialState) { self.partial = state; } #[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 { /// 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 Offset for &[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_from({snd:?}, {fst:?})` only accepts slices of `self`" ); (snd as usize - fst as usize) / crate::lib::std::mem::size_of::() } } 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 Offset for &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 Offset for &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 Offset for &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 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 Offset<<(I, usize) as Stream>::Checkpoint> for (I, usize) where I: Stream + Clone, { #[inline(always)] fn offset_from(&self, other: &<(I, usize) as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl Offset for LocatingSlice where I: Stream, { #[inline(always)] fn offset_from(&self, other: &Self) -> usize { self.offset_from(&other.checkpoint()) } } impl Offset< as Stream>::Checkpoint> for LocatingSlice where I: Stream, { #[inline(always)] fn offset_from(&self, other: & as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Offset for Recoverable where I: Stream, E: crate::lib::std::fmt::Debug, { #[inline(always)] fn offset_from(&self, other: &Self) -> usize { self.offset_from(&other.checkpoint()) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Offset< as Stream>::Checkpoint> for Recoverable where I: Stream, E: crate::lib::std::fmt::Debug, { #[inline(always)] fn offset_from(&self, other: & as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl Offset for Stateful 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 Offset< as Stream>::Checkpoint> for Stateful where I: Stream, S: crate::lib::std::fmt::Debug, { #[inline(always)] fn offset_from(&self, other: & as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl Offset for Partial where I: Stream, { #[inline(always)] fn offset_from(&self, start: &Self) -> usize { self.offset_from(&start.checkpoint()) } } impl Offset< as Stream>::Checkpoint> for Partial where I: Stream, { #[inline(always)] fn offset_from(&self, other: & as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl Offset for Checkpoint where I: Offset, { #[inline(always)] fn offset_from(&self, start: &Self) -> usize { self.inner.offset_from(&start.inner) } } /// 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 AsBytes for &[u8] { #[inline(always)] fn as_bytes(&self) -> &[u8] { self } } impl AsBytes for &Bytes { #[inline(always)] fn as_bytes(&self) -> &[u8] { (*self).as_bytes() } } impl AsBytes for LocatingSlice where I: AsBytes, { #[inline(always)] fn as_bytes(&self) -> &[u8] { self.input.as_bytes() } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl AsBytes for Recoverable where I: Stream, I: AsBytes, { #[inline(always)] fn as_bytes(&self) -> &[u8] { self.input.as_bytes() } } impl AsBytes for Stateful where I: AsBytes, { #[inline(always)] fn as_bytes(&self) -> &[u8] { self.input.as_bytes() } } impl AsBytes for Partial 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 AsBStr for &[u8] { #[inline(always)] fn as_bstr(&self) -> &[u8] { self } } impl AsBStr for &BStr { #[inline(always)] fn as_bstr(&self) -> &[u8] { (*self).as_bytes() } } impl AsBStr for &str { #[inline(always)] fn as_bstr(&self) -> &[u8] { (*self).as_bytes() } } impl AsBStr for LocatingSlice where I: AsBStr, { #[inline(always)] fn as_bstr(&self) -> &[u8] { self.input.as_bstr() } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl AsBStr for Recoverable where I: Stream, I: AsBStr, { #[inline(always)] fn as_bstr(&self) -> &[u8] { self.input.as_bstr() } } impl AsBStr for Stateful where I: AsBStr, { #[inline(always)] fn as_bstr(&self) -> &[u8] { self.input.as_bstr() } } impl AsBStr for Partial 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 /// /// `usize` is the end of the successful match within the buffer. /// This is most relevant for caseless UTF-8 where `Compare::compare`'s parameter might be a different /// length than the match within the buffer. Ok(usize), /// We need more data to be sure Incomplete, /// Comparison failed Error, } /// Abstracts comparison operations pub trait Compare { /// Compares self to another value for equality fn compare(&self, t: T) -> CompareResult; } impl<'b> Compare<&'b [u8]> for &[u8] { #[inline] fn compare(&self, t: &'b [u8]) -> CompareResult { if t.iter().zip(*self).any(|(a, b)| a != b) { CompareResult::Error } else if self.len() < t.slice_len() { CompareResult::Incomplete } else { CompareResult::Ok(t.slice_len()) } } } impl<'b> Compare> for &[u8] { #[inline] fn compare(&self, t: AsciiCaseless<&'b [u8]>) -> CompareResult { if t.0 .iter() .zip(*self) .any(|(a, b)| !a.eq_ignore_ascii_case(b)) { CompareResult::Error } else if self.len() < t.slice_len() { CompareResult::Incomplete } else { CompareResult::Ok(t.slice_len()) } } } impl Compare<[u8; LEN]> for &[u8] { #[inline(always)] fn compare(&self, t: [u8; LEN]) -> CompareResult { self.compare(&t[..]) } } impl Compare> for &[u8] { #[inline(always)] fn compare(&self, t: AsciiCaseless<[u8; LEN]>) -> CompareResult { self.compare(AsciiCaseless(&t.0[..])) } } impl<'b, const LEN: usize> Compare<&'b [u8; LEN]> for &[u8] { #[inline(always)] fn compare(&self, t: &'b [u8; LEN]) -> CompareResult { self.compare(&t[..]) } } impl<'b, const LEN: usize> Compare> for &[u8] { #[inline(always)] fn compare(&self, t: AsciiCaseless<&'b [u8; LEN]>) -> CompareResult { self.compare(AsciiCaseless(&t.0[..])) } } impl<'b> Compare<&'b str> for &[u8] { #[inline(always)] fn compare(&self, t: &'b str) -> CompareResult { self.compare(t.as_bytes()) } } impl<'b> Compare> for &[u8] { #[inline(always)] fn compare(&self, t: AsciiCaseless<&'b str>) -> CompareResult { self.compare(AsciiCaseless(t.0.as_bytes())) } } impl Compare for &[u8] { #[inline] fn compare(&self, t: u8) -> CompareResult { match self.first().copied() { Some(c) if t == c => CompareResult::Ok(t.slice_len()), Some(_) => CompareResult::Error, None => CompareResult::Incomplete, } } } impl Compare> for &[u8] { #[inline] fn compare(&self, t: AsciiCaseless) -> CompareResult { match self.first() { Some(c) if t.0.eq_ignore_ascii_case(c) => CompareResult::Ok(t.slice_len()), Some(_) => CompareResult::Error, None => CompareResult::Incomplete, } } } impl Compare for &[u8] { #[inline(always)] fn compare(&self, t: char) -> CompareResult { self.compare(t.encode_utf8(&mut [0; 4]).as_bytes()) } } impl Compare> for &[u8] { #[inline(always)] fn compare(&self, t: AsciiCaseless) -> CompareResult { self.compare(AsciiCaseless(t.0.encode_utf8(&mut [0; 4]).as_bytes())) } } impl<'b> Compare<&'b str> for &str { #[inline(always)] fn compare(&self, t: &'b str) -> CompareResult { self.as_bytes().compare(t.as_bytes()) } } impl<'b> Compare> for &str { #[inline(always)] fn compare(&self, t: AsciiCaseless<&'b str>) -> CompareResult { self.as_bytes().compare(t.as_bytes()) } } impl Compare for &str { #[inline(always)] fn compare(&self, t: char) -> CompareResult { self.as_bytes().compare(t) } } impl Compare> for &str { #[inline(always)] fn compare(&self, t: AsciiCaseless) -> CompareResult { self.as_bytes().compare(t) } } impl<'a, T> Compare for &'a Bytes where &'a [u8]: Compare, { #[inline(always)] fn compare(&self, t: T) -> CompareResult { let bytes = (*self).as_bytes(); bytes.compare(t) } } impl<'a, T> Compare for &'a BStr where &'a [u8]: Compare, { #[inline(always)] fn compare(&self, t: T) -> CompareResult { let bytes = (*self).as_bytes(); bytes.compare(t) } } impl Compare for LocatingSlice where I: Compare, { #[inline(always)] fn compare(&self, other: U) -> CompareResult { self.input.compare(other) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl Compare for Recoverable where I: Stream, I: Compare, { #[inline(always)] fn compare(&self, other: U) -> CompareResult { self.input.compare(other) } } impl Compare for Stateful where I: Compare, { #[inline(always)] fn compare(&self, other: U) -> CompareResult { self.input.compare(other) } } impl Compare for Partial where I: Compare, { #[inline(always)] fn compare(&self, t: T) -> CompareResult { self.input.compare(t) } } /// Look for a slice in self pub trait FindSlice { /// Returns the offset of the slice if it is found fn find_slice(&self, substr: T) -> Option>; } impl<'s> FindSlice<&'s [u8]> for &[u8] { #[inline(always)] fn find_slice(&self, substr: &'s [u8]) -> Option> { memmem(self, substr) } } impl<'s> FindSlice<(&'s [u8],)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (&'s [u8],)) -> Option> { memmem(self, substr.0) } } impl<'s> FindSlice<(&'s [u8], &'s [u8])> for &[u8] { #[inline(always)] fn find_slice( &self, substr: (&'s [u8], &'s [u8]), ) -> Option> { memmem2(self, substr) } } impl<'s> FindSlice<(&'s [u8], &'s [u8], &'s [u8])> for &[u8] { #[inline(always)] fn find_slice( &self, substr: (&'s [u8], &'s [u8], &'s [u8]), ) -> Option> { memmem3(self, substr) } } impl FindSlice for &[u8] { #[inline(always)] fn find_slice(&self, substr: char) -> Option> { let mut b = [0; 4]; let substr = substr.encode_utf8(&mut b); self.find_slice(&*substr) } } impl FindSlice<(char,)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (char,)) -> Option> { let mut b = [0; 4]; let substr0 = substr.0.encode_utf8(&mut b); self.find_slice((&*substr0,)) } } impl FindSlice<(char, char)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (char, char)) -> Option> { let mut b = [0; 4]; let substr0 = substr.0.encode_utf8(&mut b); let mut b = [0; 4]; let substr1 = substr.1.encode_utf8(&mut b); self.find_slice((&*substr0, &*substr1)) } } impl FindSlice<(char, char, char)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (char, char, char)) -> Option> { let mut b = [0; 4]; let substr0 = substr.0.encode_utf8(&mut b); let mut b = [0; 4]; let substr1 = substr.1.encode_utf8(&mut b); let mut b = [0; 4]; let substr2 = substr.2.encode_utf8(&mut b); self.find_slice((&*substr0, &*substr1, &*substr2)) } } impl FindSlice for &[u8] { #[inline(always)] fn find_slice(&self, substr: u8) -> Option> { memchr(substr, self).map(|i| i..i + 1) } } impl FindSlice<(u8,)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (u8,)) -> Option> { memchr(substr.0, self).map(|i| i..i + 1) } } impl FindSlice<(u8, u8)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (u8, u8)) -> Option> { memchr2(substr, self).map(|i| i..i + 1) } } impl FindSlice<(u8, u8, u8)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (u8, u8, u8)) -> Option> { memchr3(substr, self).map(|i| i..i + 1) } } impl<'s> FindSlice<&'s str> for &[u8] { #[inline(always)] fn find_slice(&self, substr: &'s str) -> Option> { self.find_slice(substr.as_bytes()) } } impl<'s> FindSlice<(&'s str,)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (&'s str,)) -> Option> { memmem(self, substr.0.as_bytes()) } } impl<'s> FindSlice<(&'s str, &'s str)> for &[u8] { #[inline(always)] fn find_slice(&self, substr: (&'s str, &'s str)) -> Option> { memmem2(self, (substr.0.as_bytes(), substr.1.as_bytes())) } } impl<'s> FindSlice<(&'s str, &'s str, &'s str)> for &[u8] { #[inline(always)] fn find_slice( &self, substr: (&'s str, &'s str, &'s str), ) -> Option> { memmem3( self, ( substr.0.as_bytes(), substr.1.as_bytes(), substr.2.as_bytes(), ), ) } } impl<'s> FindSlice<&'s str> for &str { #[inline(always)] fn find_slice(&self, substr: &'s str) -> Option> { self.as_bytes().find_slice(substr) } } impl<'s> FindSlice<(&'s str,)> for &str { #[inline(always)] fn find_slice(&self, substr: (&'s str,)) -> Option> { self.as_bytes().find_slice(substr) } } impl<'s> FindSlice<(&'s str, &'s str)> for &str { #[inline(always)] fn find_slice(&self, substr: (&'s str, &'s str)) -> Option> { self.as_bytes().find_slice(substr) } } impl<'s> FindSlice<(&'s str, &'s str, &'s str)> for &str { #[inline(always)] fn find_slice( &self, substr: (&'s str, &'s str, &'s str), ) -> Option> { self.as_bytes().find_slice(substr) } } impl FindSlice for &str { #[inline(always)] fn find_slice(&self, substr: char) -> Option> { self.as_bytes().find_slice(substr) } } impl FindSlice<(char,)> for &str { #[inline(always)] fn find_slice(&self, substr: (char,)) -> Option> { self.as_bytes().find_slice(substr) } } impl FindSlice<(char, char)> for &str { #[inline(always)] fn find_slice(&self, substr: (char, char)) -> Option> { self.as_bytes().find_slice(substr) } } impl FindSlice<(char, char, char)> for &str { #[inline(always)] fn find_slice(&self, substr: (char, char, char)) -> Option> { self.as_bytes().find_slice(substr) } } impl<'i, S> FindSlice for &'i Bytes where &'i [u8]: FindSlice, { #[inline(always)] fn find_slice(&self, substr: S) -> Option> { let bytes = (*self).as_bytes(); let offset = bytes.find_slice(substr); offset } } impl<'i, S> FindSlice for &'i BStr where &'i [u8]: FindSlice, { #[inline(always)] fn find_slice(&self, substr: S) -> Option> { let bytes = (*self).as_bytes(); let offset = bytes.find_slice(substr); offset } } impl FindSlice for LocatingSlice where I: FindSlice, { #[inline(always)] fn find_slice(&self, substr: T) -> Option> { self.input.find_slice(substr) } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl FindSlice for Recoverable where I: Stream, I: FindSlice, { #[inline(always)] fn find_slice(&self, substr: T) -> Option> { self.input.find_slice(substr) } } impl FindSlice for Stateful where I: FindSlice, { #[inline(always)] fn find_slice(&self, substr: T) -> Option> { self.input.find_slice(substr) } } impl FindSlice for Partial where I: FindSlice, { #[inline(always)] fn find_slice(&self, substr: T) -> Option> { self.input.find_slice(substr) } } /// Used to integrate `str`'s `parse()` method pub trait ParseSlice { /// Succeeds if `parse()` succeeded /// /// The byte slice implementation will first convert it to a `&str`, then apply the `parse()` /// function fn parse_slice(&self) -> Option; } impl ParseSlice for &[u8] { #[inline(always)] fn parse_slice(&self) -> Option { from_utf8(self).ok().and_then(|s| s.parse().ok()) } } impl ParseSlice for &str { #[inline(always)] fn parse_slice(&self) -> Option { 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 UpdateSlice for &[T] where T: Clone + crate::lib::std::fmt::Debug, { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { inner } } impl UpdateSlice for &str { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { inner } } impl UpdateSlice for &Bytes { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { Bytes::new(inner) } } impl UpdateSlice for &BStr { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { BStr::new(inner) } } impl UpdateSlice for LocatingSlice where I: UpdateSlice, { #[inline(always)] fn update_slice(mut self, inner: Self::Slice) -> Self { self.input = I::update_slice(self.input, inner); self } } #[cfg(feature = "unstable-recover")] #[cfg(feature = "std")] impl UpdateSlice for Recoverable where I: Stream, I: UpdateSlice, E: 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 UpdateSlice for Stateful 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 UpdateSlice for Partial 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 private pub struct Checkpoint { inner: T, stream: core::marker::PhantomData, } impl Checkpoint { fn new(inner: T) -> Self { Self { inner, stream: Default::default(), } } } impl Copy for Checkpoint {} impl Clone for Checkpoint { #[inline(always)] fn clone(&self) -> Self { Self { inner: self.inner.clone(), stream: Default::default(), } } } impl PartialOrd for Checkpoint { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { self.inner.partial_cmp(&other.inner) } } impl Ord for Checkpoint { #[inline(always)] fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.inner.cmp(&other.inner) } } impl PartialEq for Checkpoint { #[inline(always)] fn eq(&self, other: &Self) -> bool { self.inner.eq(&other.inner) } } impl Eq for Checkpoint {} impl crate::lib::std::fmt::Debug for Checkpoint { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { self.inner.fmt(f) } } /// A range bounded inclusively for counting parses performed /// /// This is flexible in what can be converted to a [Range]: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::prelude::*; /// # use winnow::token::any; /// # use winnow::combinator::repeat; /// # fn inner(input: &mut &str) -> PResult { /// # any.parse_next(input) /// # } /// # let mut input = "0123456789012345678901234567890123456789"; /// # let input = &mut input; /// let parser: Vec<_> = repeat(5, inner).parse_next(input).unwrap(); /// # let mut input = "0123456789012345678901234567890123456789"; /// # let input = &mut input; /// let parser: Vec<_> = repeat(.., inner).parse_next(input).unwrap(); /// # let mut input = "0123456789012345678901234567890123456789"; /// # let input = &mut input; /// let parser: Vec<_> = repeat(1.., inner).parse_next(input).unwrap(); /// # let mut input = "0123456789012345678901234567890123456789"; /// # let input = &mut input; /// let parser: Vec<_> = repeat(5..8, inner).parse_next(input).unwrap(); /// # let mut input = "0123456789012345678901234567890123456789"; /// # let input = &mut input; /// let parser: Vec<_> = repeat(5..=8, inner).parse_next(input).unwrap(); /// # } /// ``` #[derive(PartialEq, Eq)] pub struct Range { pub(crate) start_inclusive: usize, pub(crate) end_inclusive: Option, } impl Range { #[inline(always)] fn raw(start_inclusive: usize, end_inclusive: Option) -> Self { Self { start_inclusive, end_inclusive, } } } impl crate::lib::std::ops::RangeBounds 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 for Range { #[inline(always)] fn from(fixed: usize) -> Self { (fixed..=fixed).into() } } impl From> for Range { #[inline(always)] fn from(range: crate::lib::std::ops::Range) -> Self { let start_inclusive = range.start; let end_inclusive = Some(range.end.saturating_sub(1)); Self::raw(start_inclusive, end_inclusive) } } impl From 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> for Range { #[inline(always)] fn from(range: crate::lib::std::ops::RangeFrom) -> Self { let start_inclusive = range.start; let end_inclusive = None; Self::raw(start_inclusive, end_inclusive) } } impl From> for Range { #[inline(always)] fn from(range: crate::lib::std::ops::RangeTo) -> Self { let start_inclusive = 0; let end_inclusive = Some(range.end.saturating_sub(1)); Self::raw(start_inclusive, end_inclusive) } } impl From> for Range { #[inline(always)] fn from(range: crate::lib::std::ops::RangeInclusive) -> Self { let start_inclusive = *range.start(); let end_inclusive = Some(*range.end()); Self::raw(start_inclusive, end_inclusive) } } impl From> for Range { #[inline(always)] fn from(range: crate::lib::std::ops::RangeToInclusive) -> 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: Sized { /// Create a new `Extend` of the correct type fn initial(capacity: Option) -> Self; /// Accumulate the input into an accumulator fn accumulate(&mut self, acc: T); } impl Accumulate for () { #[inline(always)] fn initial(_capacity: Option) -> Self {} #[inline(always)] fn accumulate(&mut self, _acc: T) {} } impl Accumulate for usize { #[inline(always)] fn initial(_capacity: Option) -> Self { 0 } #[inline(always)] fn accumulate(&mut self, _acc: T) { *self += 1; } } #[cfg(feature = "alloc")] impl Accumulate for Vec { #[inline(always)] fn initial(capacity: Option) -> Self { match capacity { Some(capacity) => Vec::with_capacity(clamp_capacity::(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 { #[inline(always)] fn initial(capacity: Option) -> Self { match capacity { Some(capacity) => Vec::with_capacity(clamp_capacity::(capacity)), None => Vec::new(), } } #[inline(always)] fn accumulate(&mut self, acc: &'i [T]) { self.extend(acc.iter().cloned()); } } #[cfg(feature = "alloc")] impl Accumulate for String { #[inline(always)] fn initial(capacity: Option) -> Self { match capacity { Some(capacity) => String::with_capacity(clamp_capacity::(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) -> Self { match capacity { Some(capacity) => String::with_capacity(clamp_capacity::(capacity)), None => String::new(), } } #[inline(always)] fn accumulate(&mut self, acc: &'i str) { self.push_str(acc); } } #[cfg(feature = "alloc")] impl Accumulate<(K, V)> for BTreeMap where K: crate::lib::std::cmp::Ord, { #[inline(always)] fn initial(_capacity: Option) -> Self { BTreeMap::new() } #[inline(always)] fn accumulate(&mut self, (key, value): (K, V)) { self.insert(key, value); } } #[cfg(feature = "std")] impl Accumulate<(K, V)> for HashMap where K: crate::lib::std::cmp::Eq + crate::lib::std::hash::Hash, S: BuildHasher + Default, { #[inline(always)] fn initial(capacity: Option) -> Self { let h = S::default(); match capacity { Some(capacity) => { HashMap::with_capacity_and_hasher(clamp_capacity::<(K, V)>(capacity), h) } None => HashMap::with_hasher(h), } } #[inline(always)] fn accumulate(&mut self, (key, value): (K, V)) { self.insert(key, value); } } #[cfg(feature = "alloc")] impl Accumulate for BTreeSet where K: crate::lib::std::cmp::Ord, { #[inline(always)] fn initial(_capacity: Option) -> Self { BTreeSet::new() } #[inline(always)] fn accumulate(&mut self, key: K) { self.insert(key); } } #[cfg(feature = "std")] impl Accumulate for HashSet where K: crate::lib::std::cmp::Eq + crate::lib::std::hash::Hash, S: BuildHasher + Default, { #[inline(always)] fn initial(capacity: Option) -> Self { let h = S::default(); match capacity { Some(capacity) => HashSet::with_capacity_and_hasher(clamp_capacity::(capacity), h), None => HashSet::with_hasher(h), } } #[inline(always)] fn accumulate(&mut self, key: K) { self.insert(key); } } #[cfg(feature = "alloc")] #[inline] pub(crate) fn clamp_capacity(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::().max(1); capacity.min(max_initial_capacity) } /// Helper trait to convert numbers to usize. /// /// By default, usize implements `From` and `From` but not /// `From` and `From` 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 matches 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 AsChar for &u8 { #[inline(always)] fn as_char(self) -> char { (*self).as_char() } #[inline(always)] fn is_alpha(self) -> bool { (*self).is_alpha() } #[inline(always)] fn is_alphanum(self) -> bool { (*self).is_alphanum() } #[inline(always)] fn is_dec_digit(self) -> bool { (*self).is_dec_digit() } #[inline(always)] fn is_hex_digit(self) -> bool { (*self).is_hex_digit() } #[inline(always)] fn is_oct_digit(self) -> bool { (*self).is_oct_digit() } #[inline(always)] fn len(self) -> usize { (*self).len() } #[inline(always)] fn is_space(self) -> bool { (*self).is_space() } #[inline(always)] fn is_newline(self) -> bool { (*self).is_newline() } } 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 AsChar for &char { #[inline(always)] fn as_char(self) -> char { (*self).as_char() } #[inline(always)] fn is_alpha(self) -> bool { (*self).is_alpha() } #[inline(always)] fn is_alphanum(self) -> bool { (*self).is_alphanum() } #[inline(always)] fn is_dec_digit(self) -> bool { (*self).is_dec_digit() } #[inline(always)] fn is_hex_digit(self) -> bool { (*self).is_hex_digit() } #[inline(always)] fn is_oct_digit(self) -> bool { (*self).is_oct_digit() } #[inline(always)] fn len(self) -> usize { (*self).len() } #[inline(always)] fn is_space(self) -> bool { (*self).is_space() } #[inline(always)] fn is_newline(self) -> bool { (*self).is_newline() } } /// Check if a token is in a set of possible tokens /// /// While this can be implemented manually, you can also build up sets using: /// - `b'c'` and `'c'` /// - `b""` /// - `|c| true` /// - `b'a'..=b'z'`, `'a'..='z'` (etc for each [range type][std::ops]) /// - `(set1, set2, ...)` /// /// # 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 { /// Returns true if self contains the token fn contains_token(&self, token: T) -> bool; } impl ContainsToken for u8 { #[inline(always)] fn contains_token(&self, token: u8) -> bool { *self == token } } impl ContainsToken<&u8> for u8 { #[inline(always)] fn contains_token(&self, token: &u8) -> bool { self.contains_token(*token) } } impl ContainsToken for u8 { #[inline(always)] fn contains_token(&self, token: char) -> bool { self.as_char() == token } } impl ContainsToken<&char> for u8 { #[inline(always)] fn contains_token(&self, token: &char) -> bool { self.contains_token(*token) } } impl ContainsToken for char { #[inline(always)] fn contains_token(&self, token: C) -> bool { *self == token.as_char() } } impl bool> ContainsToken for F { #[inline(always)] fn contains_token(&self, token: C) -> bool { self(token) } } impl ContainsToken for crate::lib::std::ops::Range { #[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 ContainsToken for crate::lib::std::ops::RangeInclusive { #[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 ContainsToken for crate::lib::std::ops::RangeFrom { #[inline(always)] fn contains_token(&self, token: C1) -> bool { let start = self.start.clone().as_char(); (start..).contains(&token.as_char()) } } impl ContainsToken for crate::lib::std::ops::RangeTo { #[inline(always)] fn contains_token(&self, token: C1) -> bool { let end = self.end.clone().as_char(); (..end).contains(&token.as_char()) } } impl ContainsToken for crate::lib::std::ops::RangeToInclusive { #[inline(always)] fn contains_token(&self, token: C1) -> bool { let end = self.end.clone().as_char(); (..=end).contains(&token.as_char()) } } impl ContainsToken for crate::lib::std::ops::RangeFull { #[inline(always)] fn contains_token(&self, _token: C1) -> bool { true } } impl ContainsToken for &'_ [u8] { #[inline] fn contains_token(&self, token: C) -> bool { let token = token.as_char(); self.iter().any(|t| t.as_char() == token) } } impl ContainsToken for &'_ [char] { #[inline] fn contains_token(&self, token: C) -> bool { let token = token.as_char(); self.iter().any(|t| *t == token) } } impl ContainsToken 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 ContainsToken for &'_ [char; LEN] { #[inline] fn contains_token(&self, token: C) -> bool { let token = token.as_char(); self.iter().any(|t| *t == token) } } impl ContainsToken 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 ContainsToken for [char; LEN] { #[inline] fn contains_token(&self, token: C) -> bool { let token = token.as_char(); self.iter().any(|t| *t == token) } } impl ContainsToken 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 ContainsToken for ($($haystack),+,) where T: Clone, $($haystack: ContainsToken),+ { #[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 { memchr::memchr(token, slice) } #[cfg(feature = "simd")] #[inline(always)] fn memchr2(token: (u8, u8), slice: &[u8]) -> Option { memchr::memchr2(token.0, token.1, slice) } #[cfg(feature = "simd")] #[inline(always)] fn memchr3(token: (u8, u8, u8), slice: &[u8]) -> Option { memchr::memchr3(token.0, token.1, token.2, slice) } #[cfg(not(feature = "simd"))] #[inline(always)] fn memchr(token: u8, slice: &[u8]) -> Option { slice.iter().position(|t| *t == token) } #[cfg(not(feature = "simd"))] #[inline(always)] fn memchr2(token: (u8, u8), slice: &[u8]) -> Option { slice.iter().position(|t| *t == token.0 || *t == token.1) } #[cfg(not(feature = "simd"))] #[inline(always)] fn memchr3(token: (u8, u8, u8), slice: &[u8]) -> Option { slice .iter() .position(|t| *t == token.0 || *t == token.1 || *t == token.2) } #[inline(always)] fn memmem(slice: &[u8], literal: &[u8]) -> Option> { match literal.len() { 0 => Some(0..0), 1 => memchr(literal[0], slice).map(|i| i..i + 1), _ => memmem_(slice, literal), } } #[inline(always)] fn memmem2(slice: &[u8], literal: (&[u8], &[u8])) -> Option> { match (literal.0.len(), literal.1.len()) { (0, _) | (_, 0) => Some(0..0), (1, 1) => memchr2((literal.0[0], literal.1[0]), slice).map(|i| i..i + 1), _ => memmem2_(slice, literal), } } #[inline(always)] fn memmem3( slice: &[u8], literal: (&[u8], &[u8], &[u8]), ) -> Option> { match (literal.0.len(), literal.1.len(), literal.2.len()) { (0, _, _) | (_, 0, _) | (_, _, 0) => Some(0..0), (1, 1, 1) => memchr3((literal.0[0], literal.1[0], literal.2[0]), slice).map(|i| i..i + 1), _ => memmem3_(slice, literal), } } #[cfg(feature = "simd")] #[inline(always)] fn memmem_(slice: &[u8], literal: &[u8]) -> Option> { let &prefix = match literal.first() { Some(x) => x, None => return Some(0..0), }; #[allow(clippy::manual_find)] // faster this way for i in memchr::memchr_iter(prefix, slice) { if slice[i..].starts_with(literal) { let i_end = i + literal.len(); return Some(i..i_end); } } None } #[cfg(feature = "simd")] fn memmem2_(slice: &[u8], literal: (&[u8], &[u8])) -> Option> { let prefix = match (literal.0.first(), literal.1.first()) { (Some(&a), Some(&b)) => (a, b), _ => return Some(0..0), }; #[allow(clippy::manual_find)] // faster this way for i in memchr::memchr2_iter(prefix.0, prefix.1, slice) { let subslice = &slice[i..]; if subslice.starts_with(literal.0) { let i_end = i + literal.0.len(); return Some(i..i_end); } if subslice.starts_with(literal.1) { let i_end = i + literal.1.len(); return Some(i..i_end); } } None } #[cfg(feature = "simd")] fn memmem3_( slice: &[u8], literal: (&[u8], &[u8], &[u8]), ) -> Option> { let prefix = match (literal.0.first(), literal.1.first(), literal.2.first()) { (Some(&a), Some(&b), Some(&c)) => (a, b, c), _ => return Some(0..0), }; #[allow(clippy::manual_find)] // faster this way for i in memchr::memchr3_iter(prefix.0, prefix.1, prefix.2, slice) { let subslice = &slice[i..]; if subslice.starts_with(literal.0) { let i_end = i + literal.0.len(); return Some(i..i_end); } if subslice.starts_with(literal.1) { let i_end = i + literal.1.len(); return Some(i..i_end); } if subslice.starts_with(literal.2) { let i_end = i + literal.2.len(); return Some(i..i_end); } } None } #[cfg(not(feature = "simd"))] fn memmem_(slice: &[u8], literal: &[u8]) -> Option> { for i in 0..slice.len() { let subslice = &slice[i..]; if subslice.starts_with(literal) { let i_end = i + literal.len(); return Some(i..i_end); } } None } #[cfg(not(feature = "simd"))] fn memmem2_(slice: &[u8], literal: (&[u8], &[u8])) -> Option> { for i in 0..slice.len() { let subslice = &slice[i..]; if subslice.starts_with(literal.0) { let i_end = i + literal.0.len(); return Some(i..i_end); } if subslice.starts_with(literal.1) { let i_end = i + literal.1.len(); return Some(i..i_end); } } None } #[cfg(not(feature = "simd"))] fn memmem3_( slice: &[u8], literal: (&[u8], &[u8], &[u8]), ) -> Option> { for i in 0..slice.len() { let subslice = &slice[i..]; if subslice.starts_with(literal.0) { let i_end = i + literal.0.len(); return Some(i..i_end); } if subslice.starts_with(literal.1) { let i_end = i + literal.1.len(); return Some(i..i_end); } if subslice.starts_with(literal.2) { let i_end = i + literal.2.len(); return Some(i..i_end); } } None } winnow-0.6.26/src/stream/tests.rs000064400000000000000000000142461046102023000150410ustar 00000000000000#[cfg(feature = "std")] use proptest::prelude::*; use crate::error::ErrMode::Backtrack; use crate::error::{ErrorKind, InputError}; use crate::token::literal; use crate::{ combinator::{separated, separated_pair}, PResult, Parser, }; use super::*; #[cfg(feature = "std")] #[test] fn test_fxhashmap_compiles() { let input = "a=b"; fn pair(i: &mut &str) -> PResult<(char, char)> { let out = separated_pair('a', '=', 'b').parse_next(i)?; Ok(out) } let _: rustc_hash::FxHashMap = separated(0.., pair, ',').parse(input).unwrap(); } #[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::>(); 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); } #[test] fn test_literal_support_char() { assert_eq!( literal::<_, _, InputError<_>>('π').parse_peek("π"), Ok(("", "π")) ); assert_eq!( literal::<_, _, InputError<_>>('π').parse_peek("π3.14"), Ok(("3.14", "π")) ); assert_eq!( literal::<_, _, InputError<_>>("π").parse_peek("π3.14"), Ok(("3.14", "π")) ); assert_eq!( literal::<_, _, InputError<_>>('-').parse_peek("π"), Err(Backtrack(InputError::new("π", ErrorKind::Tag))) ); assert_eq!( literal::<_, Partial<&[u8]>, InputError<_>>('π').parse_peek(Partial::new(b"\xCF\x80")), Ok((Partial::new(Default::default()), "π".as_bytes())) ); assert_eq!( literal::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x80"), Ok((Default::default(), "π".as_bytes())) ); assert_eq!( literal::<_, Partial<&[u8]>, InputError<_>>('π').parse_peek(Partial::new(b"\xCF\x803.14")), Ok((Partial::new(&b"3.14"[..]), "π".as_bytes())) ); assert_eq!( literal::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x80"), Ok((Default::default(), "π".as_bytes())) ); assert_eq!( literal::<_, &[u8], InputError<_>>('π').parse_peek(b"\xCF\x803.14"), Ok((&b"3.14"[..], "π".as_bytes())) ); assert_eq!( literal::<_, &[u8], InputError<_>>(AsciiCaseless('a')).parse_peek(b"ABCxyz"), Ok((&b"BCxyz"[..], &b"A"[..])) ); assert_eq!( literal::<_, &[u8], InputError<_>>('a').parse_peek(b"ABCxyz"), Err(Backtrack(InputError::new(&b"ABCxyz"[..], ErrorKind::Tag))) ); assert_eq!( literal::<_, &[u8], InputError<_>>(AsciiCaseless('π')).parse_peek(b"\xCF\x803.14"), Ok((&b"3.14"[..], "π".as_bytes())) ); assert_eq!( literal::<_, _, InputError<_>>(AsciiCaseless('🧑')).parse_peek("🧑你好"), Ok(("你好", "🧑")) ); let mut buffer = [0; 4]; let input = '\u{241b}'.encode_utf8(&mut buffer); assert_eq!( literal::<_, &[u8], InputError<_>>(AsciiCaseless('␛')).parse_peek(input.as_bytes()), Ok((&b""[..], [226, 144, 155].as_slice())) ); assert_eq!( literal::<_, &[u8], InputError<_>>('-').parse_peek(b"\xCF\x80"), Err(Backtrack(InputError::new(&b"\xCF\x80"[..], ErrorKind::Tag))) ); } winnow-0.6.26/src/token/mod.rs000064400000000000000000001163331046102023000143030ustar 00000000000000//! Parsers extracting tokens from the stream #[cfg(test)] mod tests; use crate::combinator::trace; use crate::combinator::DisplayDebug; 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::PResult; use crate::Parser; /// Matches one token /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn any(input: &mut &str) -> PResult /// # { /// # winnow::token::any.parse_next(input) /// # } /// ``` /// /// # 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(input: &mut Input) -> PResult<::Token, Error> where Input: StreamIsPartial + Stream, Error: ParserError, { trace("any", move |input: &mut Input| { if ::is_partial_supported() { any_::<_, _, true>(input) } else { any_::<_, _, false>(input) } }) .parse_next(input) } fn any_, const PARTIAL: bool>( input: &mut I, ) -> PResult<::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 literal combinator's argument and will return the part of /// the input that matches the argument /// /// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the literal /// ///
/// /// **Note:** [`Parser`] is implemented for strings and byte strings as a convenience (complete /// only) /// ///
/// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// # use winnow::error::ContextError; /// pub fn literal(literal: &str) -> impl Parser<&str, &str, ContextError> /// # { /// # winnow::token::literal(literal) /// # } /// ``` /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # /// 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; /// /// fn parser(s: Partial<&str>) -> IResult, &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)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::literal; /// use winnow::ascii::Caseless; /// /// fn parser(s: &str) -> IResult<&str, &str> { /// literal(Caseless("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)))); /// ``` #[inline(always)] #[doc(alias = "tag")] #[doc(alias = "bytes")] #[doc(alias = "just")] pub fn literal( literal: Literal, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream + Compare, Literal: SliceLen + Clone + crate::lib::std::fmt::Debug, Error: ParserError, { trace(DisplayDebug(literal.clone()), move |i: &mut Input| { let t = literal.clone(); if ::is_partial_supported() { literal_::<_, _, _, true>(i, t) } else { literal_::<_, _, _, false>(i, t) } }) } fn literal_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + Compare, T: SliceLen + crate::lib::std::fmt::Debug, { let literal_len = t.slice_len(); match i.compare(t) { CompareResult::Ok(len) => Ok(i.next_slice(len)), CompareResult::Incomplete if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete( Needed::new(literal_len - i.eof_offset()), )), CompareResult::Incomplete | CompareResult::Error => { let e: ErrorKind = ErrorKind::Tag; Err(ErrMode::from_error_kind(i, e)) } } } /// Recognize a token that matches a [set of tokens][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][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// # use winnow::stream::ContainsToken; /// # use winnow::error::ContextError; /// pub fn one_of<'i>(set: impl ContainsToken) -> impl Parser<&'i str, char, ContextError> /// # { /// # winnow::token::one_of(set) /// # } /// ``` /// /// # 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, 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(set: Set) -> impl Parser::Token, Error> where Input: StreamIsPartial + Stream, ::Token: Clone, Set: ContainsToken<::Token>, Error: ParserError, { trace( "one_of", any.verify(move |t: &::Token| set.contains_token(t.clone())), ) } /// Recognize a token that does not match a [set of tokens][ContainsToken] /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *[Partial version][crate::_topic::partial]*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// # use winnow::stream::ContainsToken; /// # use winnow::error::ContextError; /// pub fn none_of<'i>(set: impl ContainsToken) -> impl Parser<&'i str, char, ContextError> /// # { /// # winnow::token::none_of(set) /// # } /// ``` /// /// # 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(set: Set) -> impl Parser::Token, Error> where Input: StreamIsPartial + Stream, ::Token: Clone, Set: ContainsToken<::Token>, Error: ParserError, { trace( "none_of", any.verify(move |t: &::Token| !set.contains_token(t.clone())), ) } /// Recognize the longest (m <= len <= n) input slice that matches a [set of tokens][ContainsToken] /// /// It will return an `ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice))` if the set of tokens wasn't met or is out /// of range (m <= len <= n). /// /// *[Partial version][crate::_topic::partial]* will return a `ErrMode::Incomplete(Needed::new(1))` if a member of the set of tokens reaches the end of the input or is too short. /// /// To take a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::take`]. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` [ranges][Range]: /// ```rust /// # use std::ops::RangeFrom; /// # use winnow::prelude::*; /// # use winnow::stream::ContainsToken; /// # use winnow::error::ContextError; /// pub fn take_while<'i>(occurrences: RangeFrom, set: impl ContainsToken) -> impl Parser<&'i str, &'i str, ContextError> /// # { /// # winnow::token::take_while(occurrences, set) /// # } /// ``` /// /// # 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, &[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, &[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, &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, &[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( occurrences: impl Into, set: Set, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream, Set: ContainsToken<::Token>, Error: ParserError, { let Range { start_inclusive, end_inclusive, } = occurrences.into(); trace("take_while", move |i: &mut Input| { match (start_inclusive, end_inclusive) { (0, None) => { if ::is_partial_supported() { take_till0::<_, _, _, true>(i, |c| !set.contains_token(c)) } else { take_till0::<_, _, _, false>(i, |c| !set.contains_token(c)) } } (1, None) => { if ::is_partial_supported() { take_till1::<_, _, _, true>(i, |c| !set.contains_token(c)) } else { take_till1::<_, _, _, false>(i, |c| !set.contains_token(c)) } } (start, end) => { let end = end.unwrap_or(usize::MAX); if ::is_partial_supported() { take_till_m_n::<_, _, _, true>(i, start, end, |c| !set.contains_token(c)) } else { take_till_m_n::<_, _, _, false>(i, start, end, |c| !set.contains_token(c)) } } } }) } fn take_till0, const PARTIAL: bool>( input: &mut I, predicate: P, ) -> PResult<::Slice, E> where P: Fn(I::Token) -> bool, { let offset = match input.offset_for(predicate) { Some(offset) => offset, None if PARTIAL && input.is_partial() => { return Err(ErrMode::Incomplete(Needed::new(1))); } None => input.eof_offset(), }; Ok(input.next_slice(offset)) } fn take_till1, const PARTIAL: bool>( input: &mut I, predicate: P, ) -> PResult<::Slice, E> where P: Fn(I::Token) -> bool, { let e: ErrorKind = ErrorKind::Slice; let offset = match input.offset_for(predicate) { Some(offset) => offset, None if PARTIAL && input.is_partial() => { return Err(ErrMode::Incomplete(Needed::new(1))); } None => input.eof_offset(), }; if offset == 0 { Err(ErrMode::from_error_kind(input, e)) } else { Ok(input.next_slice(offset)) } } fn take_till_m_n, const PARTIAL: bool>( input: &mut I, m: usize, n: usize, predicate: P, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, P: Fn(I::Token) -> bool, { if n < m { return Err(ErrMode::assert( input, "`occurrences` should be ascending, rather than descending", )); } let mut final_count = 0; for (processed, (offset, token)) in input.iter_offsets().enumerate() { if predicate(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 member of a [set of tokens][ContainsToken] is found. /// /// It doesn't consume the terminating token from the set. /// /// *[Partial version][crate::_topic::partial]* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the /// end of input or if there was not match. /// /// See also /// - [`take_until`] for recognizing up-to a [`literal`] (w/ optional simd optimizations) /// - [`repeat_till`][crate::combinator::repeat_till] with [`Parser::take`] for taking tokens up to a [`Parser`] /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` [ranges][Range]: /// ```rust /// # use std::ops::RangeFrom; /// # use winnow::prelude::*; /// # use winnow::stream::ContainsToken; /// # use winnow::error::ContextError; /// pub fn take_till<'i>(occurrences: RangeFrom, set: impl ContainsToken) -> impl Parser<&'i str, &'i str, ContextError> /// # { /// # winnow::token::take_till(occurrences, set) /// # } /// ``` /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_till; /// /// fn till_colon(s: &str) -> IResult<&str, &str> { /// take_till(0.., |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_till; /// /// fn till_colon(s: Partial<&str>) -> IResult, &str> { /// take_till(0.., |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)] #[doc(alias = "is_not")] pub fn take_till( occurrences: impl Into, set: Set, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream, Set: ContainsToken<::Token>, Error: ParserError, { let Range { start_inclusive, end_inclusive, } = occurrences.into(); trace("take_till", move |i: &mut Input| { match (start_inclusive, end_inclusive) { (0, None) => { if ::is_partial_supported() { take_till0::<_, _, _, true>(i, |c| set.contains_token(c)) } else { take_till0::<_, _, _, false>(i, |c| set.contains_token(c)) } } (1, None) => { if ::is_partial_supported() { take_till1::<_, _, _, true>(i, |c| set.contains_token(c)) } else { take_till1::<_, _, _, false>(i, |c| set.contains_token(c)) } } (start, end) => { let end = end.unwrap_or(usize::MAX); if ::is_partial_supported() { take_till_m_n::<_, _, _, true>(i, start, end, |c| set.contains_token(c)) } else { take_till_m_n::<_, _, _, false>(i, start, end, |c| set.contains_token(c)) } } } }) } /// Recognize an input slice containing the first N input elements (I[..N]). /// /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is shorter than the argument. /// /// *[Partial version][crate::_topic::partial]*: if the input has less than N elements, `take` will /// return a `ErrMode::Incomplete(Needed::new(M))` where M is the number of /// additional bytes the parser would need to succeed. /// It is well defined for `&[u8]` as the number of elements is the byte size, /// but for types like `&str`, we cannot know how many bytes correspond for /// the next few chars, so the result will be `ErrMode::Incomplete(Needed::Unknown)` /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` ranges: /// ```rust /// # use std::ops::RangeFrom; /// # use winnow::prelude::*; /// # use winnow::stream::ContainsToken; /// # use winnow::error::ContextError; /// pub fn take<'i>(token_count: usize) -> impl Parser<&'i str, &'i str, ContextError> /// # { /// # winnow::token::take(token_count) /// # } /// ``` /// /// # Example /// /// ```rust /// # 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, &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( token_count: UsizeLike, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream, UsizeLike: ToUsize, Error: ParserError, { let c = token_count.to_usize(); trace("take", move |i: &mut Input| { if ::is_partial_supported() { take_::<_, _, true>(i, c) } else { take_::<_, _, false>(i, c) } }) } fn take_, const PARTIAL: bool>( i: &mut I, c: usize, ) -> PResult<::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 a [literal]. /// /// Feature `simd` will enable the use of [`memchr`](https://docs.rs/memchr/latest/memchr/). /// /// It doesn't consume the literal. /// /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` /// if the literal wasn't met. /// /// *[Partial version][crate::_topic::partial]*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't /// contain the literal or if the input is smaller than the literal. /// /// See also /// - [`take_till`] for recognizing up-to a [set of tokens][ContainsToken] /// - [`repeat_till`][crate::combinator::repeat_till] with [`Parser::take`] for taking tokens up to a [`Parser`] /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream] with `0..` or `1..` [ranges][Range]: /// ```rust /// # use std::ops::RangeFrom; /// # use winnow::prelude::*;; /// # use winnow::error::ContextError; /// pub fn take_until(occurrences: RangeFrom, literal: &str) -> impl Parser<&str, &str, ContextError> /// # { /// # winnow::token::take_until(occurrences, literal) /// # } /// ``` /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_until; /// /// fn until_eof(s: &str) -> IResult<&str, &str> { /// take_until(0.., "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_until; /// /// fn until_eof(s: Partial<&str>) -> IResult, &str> { /// take_until(0.., "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"))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_until; /// /// fn until_eof(s: &str) -> IResult<&str, &str> { /// take_until(1.., "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_until; /// /// fn until_eof(s: Partial<&str>) -> IResult, &str> { /// take_until(1.., "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_until( occurrences: impl Into, literal: Literal, ) -> impl Parser::Slice, Error> where Input: StreamIsPartial + Stream + FindSlice, Literal: Clone, Error: ParserError, { let Range { start_inclusive, end_inclusive, } = occurrences.into(); trace("take_until", move |i: &mut Input| { match (start_inclusive, end_inclusive) { (0, None) => { if ::is_partial_supported() { take_until0_::<_, _, _, true>(i, literal.clone()) } else { take_until0_::<_, _, _, false>(i, literal.clone()) } } (1, None) => { if ::is_partial_supported() { take_until1_::<_, _, _, true>(i, literal.clone()) } else { take_until1_::<_, _, _, false>(i, literal.clone()) } } (start, end) => { let end = end.unwrap_or(usize::MAX); if ::is_partial_supported() { take_until_m_n_::<_, _, _, true>(i, start, end, literal.clone()) } else { take_until_m_n_::<_, _, _, false>(i, start, end, literal.clone()) } } } }) } fn take_until0_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, { match i.find_slice(t) { Some(range) => Ok(i.next_slice(range.start)), None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), } } fn take_until1_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, { match i.find_slice(t) { None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), Some(range) => { if range.start == 0 { Err(ErrMode::from_error_kind(i, ErrorKind::Slice)) } else { Ok(i.next_slice(range.start)) } } } } fn take_until_m_n_, const PARTIAL: bool>( i: &mut I, start: usize, end: usize, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, { if end < start { return Err(ErrMode::assert( i, "`occurrences` should be ascending, rather than descending", )); } match i.find_slice(t) { Some(range) => { let start_offset = i.offset_at(start); let end_offset = i.offset_at(end).unwrap_or_else(|_err| i.eof_offset()); if start_offset.map(|s| range.start < s).unwrap_or(true) { if PARTIAL && i.is_partial() { return Err(ErrMode::Incomplete(Needed::Unknown)); } else { return Err(ErrMode::from_error_kind(i, ErrorKind::Slice)); } } if end_offset < range.start { return Err(ErrMode::from_error_kind(i, ErrorKind::Slice)); } Ok(i.next_slice(range.start)) } None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), } } /// Return the remaining input. /// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn rest<'i>(input: &mut &'i str) -> PResult<&'i str> /// # { /// # winnow::token::rest.parse_next(input) /// # } /// ``` /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::error::ErrorKind; /// # use winnow::error::InputError; /// use winnow::token::rest; /// assert_eq!(rest::<_,InputError<_>>.parse_peek("abc"), Ok(("", "abc"))); /// assert_eq!(rest::<_,InputError<_>>.parse_peek(""), Ok(("", ""))); /// ``` #[inline] pub fn rest(input: &mut Input) -> PResult<::Slice, Error> where Input: Stream, Error: ParserError, { trace("rest", move |input: &mut Input| Ok(input.finish())).parse_next(input) } /// Return the length of the remaining input. /// ///
/// /// Note: this does not advance the [`Stream`] /// ///
/// /// # Effective Signature /// /// Assuming you are parsing a `&str` [Stream]: /// ```rust /// # use winnow::prelude::*;; /// pub fn rest_len(input: &mut &str) -> PResult /// # { /// # winnow::token::rest_len.parse_next(input) /// # } /// ``` /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::error::ErrorKind; /// # use winnow::error::InputError; /// use winnow::token::rest_len; /// assert_eq!(rest_len::<_,InputError<_>>.parse_peek("abc"), Ok(("abc", 3))); /// assert_eq!(rest_len::<_,InputError<_>>.parse_peek(""), Ok(("", 0))); /// ``` #[inline] pub fn rest_len(input: &mut Input) -> PResult where Input: Stream, Error: ParserError, { trace("rest_len", move |input: &mut Input| { let len = input.eof_offset(); Ok(len) }) .parse_next(input) } winnow-0.6.26/src/token/tests.rs000064400000000000000000000604101046102023000146600ustar 00000000000000use super::*; #[cfg(feature = "std")] use proptest::prelude::*; use crate::ascii::Caseless; 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::literal; use crate::unpeek; use crate::IResult; use crate::Parser; use crate::Partial; macro_rules! assert_parse( ($left: expr, $right: expr) => { let res: $crate::IResult<_, _, InputError<_>> = $left; assert_eq!(res, $right); }; ); #[test] fn complete_take_while_m_n_utf8_all_matching() { let result: IResult<&str, &str> = 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 IResult<&str, &str> { take_until(5..=8, "end").parse_peek(i) } assert_eq!( take_until_5_10("end"), Err(ErrMode::Backtrack(error_position!( &"end", ErrorKind::Slice ))) ); assert_eq!( take_until_5_10("1234end"), Err(ErrMode::Backtrack(error_position!( &"1234end", ErrorKind::Slice ))) ); assert_eq!(take_until_5_10("12345end"), Ok(("end", "12345"))); assert_eq!(take_until_5_10("123456end"), Ok(("end", "123456"))); assert_eq!(take_until_5_10("12345678end"), Ok(("end", "12345678"))); assert_eq!( take_until_5_10("123456789end"), Err(ErrMode::Backtrack(error_position!( &"123456789end", ErrorKind::Slice ))) ); } #[test] fn complete_take_until_empty() { fn take_until_empty(i: &str) -> IResult<&str, &str> { take_until(0, "").parse_peek(i) } assert_eq!(take_until_empty(""), Ok(("", ""))); assert_eq!(take_until_empty("end"), Ok(("end", ""))); } #[test] fn complete_literal_case_insensitive() { fn caseless_bytes(i: &[u8]) -> IResult<&[u8], &[u8]> { literal(Caseless("ABcd")).parse_peek(i) } assert_eq!( caseless_bytes(&b"aBCdefgh"[..]), Ok((&b"efgh"[..], &b"aBCd"[..])) ); assert_eq!( caseless_bytes(&b"abcdefgh"[..]), Ok((&b"efgh"[..], &b"abcd"[..])) ); assert_eq!( caseless_bytes(&b"ABCDefgh"[..]), Ok((&b"efgh"[..], &b"ABCD"[..])) ); assert_eq!( caseless_bytes(&b"ab"[..]), Err(ErrMode::Backtrack(error_position!( &&b"ab"[..], ErrorKind::Tag ))) ); assert_eq!( caseless_bytes(&b"Hello"[..]), Err(ErrMode::Backtrack(error_position!( &&b"Hello"[..], ErrorKind::Tag ))) ); assert_eq!( caseless_bytes(&b"Hel"[..]), Err(ErrMode::Backtrack(error_position!( &&b"Hel"[..], ErrorKind::Tag ))) ); fn caseless_str(i: &str) -> IResult<&str, &str> { literal(Caseless("ABcd")).parse_peek(i) } assert_eq!(caseless_str("aBCdefgh"), Ok(("efgh", "aBCd"))); assert_eq!(caseless_str("abcdefgh"), Ok(("efgh", "abcd"))); assert_eq!(caseless_str("ABCDefgh"), Ok(("efgh", "ABCD"))); assert_eq!( caseless_str("ab"), Err(ErrMode::Backtrack(error_position!(&"ab", ErrorKind::Tag))) ); assert_eq!( caseless_str("Hello"), Err(ErrMode::Backtrack(error_position!( &"Hello", ErrorKind::Tag ))) ); assert_eq!( caseless_str("Hel"), Err(ErrMode::Backtrack(error_position!(&"Hel", ErrorKind::Tag))) ); fn matches_kelvin(i: &str) -> IResult<&str, &str> { literal(Caseless("k")).parse_peek(i) } assert_eq!( matches_kelvin("K"), Err(ErrMode::Backtrack(error_position!(&"K", ErrorKind::Tag))) ); fn is_kelvin(i: &str) -> IResult<&str, &str> { literal(Caseless("K")).parse_peek(i) } assert_eq!( is_kelvin("k"), Err(ErrMode::Backtrack(error_position!(&"k", ErrorKind::Tag))) ); } #[test] fn complete_literal_fixed_size_array() { fn test(i: &[u8]) -> IResult<&[u8], &[u8]> { literal([0x42]).parse_peek(i) } fn test2(i: &[u8]) -> IResult<&[u8], &[u8]> { literal(&[0x42]).parse_peek(i) } let input = &[0x42, 0x00][..]; assert_eq!(test(input), Ok((&b"\x00"[..], &b"\x42"[..]))); assert_eq!(test2(input), Ok((&b"\x00"[..], &b"\x42"[..]))); } #[test] fn complete_literal_char() { fn test(i: &[u8]) -> IResult<&[u8], &[u8]> { literal('B').parse_peek(i) } assert_eq!(test(&[0x42, 0x00][..]), Ok((&b"\x00"[..], &b"\x42"[..]))); assert_eq!( test(&[b'A', b'\0'][..]), Err(ErrMode::Backtrack(error_position!( &&b"A\0"[..], ErrorKind::Tag ))) ); } #[test] fn complete_literal_byte() { fn test(i: &[u8]) -> IResult<&[u8], &[u8]> { literal(b'B').parse_peek(i) } assert_eq!(test(&[0x42, 0x00][..]), Ok((&b"\x00"[..], &b"\x42"[..]))); assert_eq!( test(&[b'A', b'\0'][..]), Err(ErrMode::Backtrack(error_position!( &&b"A\0"[..], ErrorKind::Tag ))) ); } #[test] fn partial_any_str() { use super::any; assert_eq!( any::<_, InputError>>.parse_peek(Partial::new("Ә")), Ok((Partial::new(""), 'Ә')) ); } #[test] fn partial_one_of_test() { fn f(i: Partial<&[u8]>) -> IResult, 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, 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, char> { 'c'.parse_peek(i) } let a = &b"abcd"[..]; assert_eq!( f(Partial::new(a)), Err(ErrMode::Backtrack(error_position!( &Partial::new(a), ErrorKind::Tag ))) ); let b = &b"cde"[..]; assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), 'c'))); } #[test] fn char_str() { fn f(i: Partial<&str>) -> IResult, char> { 'c'.parse_peek(i) } let a = "abcd"; assert_eq!( f(Partial::new(a)), Err(ErrMode::Backtrack(error_position!( &Partial::new(a), ErrorKind::Tag ))) ); 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, 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, &[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, &[u8]> { take_till(1.., ['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, &[u8]> { take_until(0.., "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, &str> { take_until(0.., "end").parse_peek(i) } assert_eq!( ys(Partial::new("123en")), Err(ErrMode::Incomplete(Needed::Unknown)) ); } #[test] fn partial_take() { use crate::ascii::{ alpha1 as alpha, alphanumeric1 as alphanumeric, digit1 as digit, hex_digit1 as hex_digit, multispace1 as multispace, oct_digit1 as oct_digit, space1 as space, }; fn x(i: Partial<&[u8]>) -> IResult, &[u8]> { delimited("").take().parse_peek(i) } let r = x(Partial::new(&b" aaa"[..])); assert_eq!(r, Ok((Partial::new(&b" aaa"[..]), &b""[..]))); let semicolon = &b";"[..]; fn ya(i: Partial<&[u8]>) -> IResult, &[u8]> { alpha.take().parse_peek(i) } let ra = ya(Partial::new(&b"abc;"[..])); assert_eq!(ra, Ok((Partial::new(semicolon), &b"abc"[..]))); fn yd(i: Partial<&[u8]>) -> IResult, &[u8]> { digit.take().parse_peek(i) } let rd = yd(Partial::new(&b"123;"[..])); assert_eq!(rd, Ok((Partial::new(semicolon), &b"123"[..]))); fn yhd(i: Partial<&[u8]>) -> IResult, &[u8]> { hex_digit.take().parse_peek(i) } let rhd = yhd(Partial::new(&b"123abcDEF;"[..])); assert_eq!(rhd, Ok((Partial::new(semicolon), &b"123abcDEF"[..]))); fn yod(i: Partial<&[u8]>) -> IResult, &[u8]> { oct_digit.take().parse_peek(i) } let rod = yod(Partial::new(&b"1234567;"[..])); assert_eq!(rod, Ok((Partial::new(semicolon), &b"1234567"[..]))); fn yan(i: Partial<&[u8]>) -> IResult, &[u8]> { alphanumeric.take().parse_peek(i) } let ran = yan(Partial::new(&b"123abc;"[..])); assert_eq!(ran, Ok((Partial::new(semicolon), &b"123abc"[..]))); fn ys(i: Partial<&[u8]>) -> IResult, &[u8]> { space.take().parse_peek(i) } let rs = ys(Partial::new(&b" \t;"[..])); assert_eq!(rs, Ok((Partial::new(semicolon), &b" \t"[..]))); fn yms(i: Partial<&[u8]>) -> IResult, &[u8]> { multispace.take().parse_peek(i) } let rms = yms(Partial::new(&b" \t\r\n;"[..])); assert_eq!(rms, Ok((Partial::new(semicolon), &b" \t\r\n"[..]))); } #[test] fn partial_take_while0() { fn f(i: Partial<&[u8]>) -> IResult, &[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, &[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, &[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, &[u8]> { take_till(0.., 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, &[u8]> { take_till(1.., 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, &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, &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, &str> { take_till(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, &str> { take_till(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_utf8() { fn f(i: Partial<&str>) -> IResult, &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, &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, &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, &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, &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, &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_take_take_while0() { fn x(i: Partial<&[u8]>) -> IResult, &[u8]> { take_while(0.., AsChar::is_alphanum).parse_peek(i) } fn y(i: Partial<&[u8]>) -> IResult, &[u8]> { unpeek(x).take().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_literal_case_insensitive() { fn caseless_bytes(i: Partial<&[u8]>) -> IResult, &[u8]> { literal(Caseless("ABcd")).parse_peek(i) } assert_eq!( caseless_bytes(Partial::new(&b"aBCdefgh"[..])), Ok((Partial::new(&b"efgh"[..]), &b"aBCd"[..])) ); assert_eq!( caseless_bytes(Partial::new(&b"abcdefgh"[..])), Ok((Partial::new(&b"efgh"[..]), &b"abcd"[..])) ); assert_eq!( caseless_bytes(Partial::new(&b"ABCDefgh"[..])), Ok((Partial::new(&b"efgh"[..]), &b"ABCD"[..])) ); assert_eq!( caseless_bytes(Partial::new(&b"ab"[..])), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( caseless_bytes(Partial::new(&b"Hello"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"Hello"[..]), ErrorKind::Tag ))) ); assert_eq!( caseless_bytes(Partial::new(&b"Hel"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"Hel"[..]), ErrorKind::Tag ))) ); fn caseless_str(i: Partial<&str>) -> IResult, &str> { literal(Caseless("ABcd")).parse_peek(i) } assert_eq!( caseless_str(Partial::new("aBCdefgh")), Ok((Partial::new("efgh"), "aBCd")) ); assert_eq!( caseless_str(Partial::new("abcdefgh")), Ok((Partial::new("efgh"), "abcd")) ); assert_eq!( caseless_str(Partial::new("ABCDefgh")), Ok((Partial::new("efgh"), "ABCD")) ); assert_eq!( caseless_str(Partial::new("ab")), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( caseless_str(Partial::new("Hello")), Err(ErrMode::Backtrack(error_position!( &Partial::new("Hello"), ErrorKind::Tag ))) ); assert_eq!( caseless_str(Partial::new("Hel")), Err(ErrMode::Backtrack(error_position!( &Partial::new("Hel"), ErrorKind::Tag ))) ); fn matches_kelvin(i: Partial<&str>) -> IResult, &str> { literal(Caseless("k")).parse_peek(i) } assert_eq!( matches_kelvin(Partial::new("K")), Err(ErrMode::Backtrack(error_position!( &Partial::new("K"), ErrorKind::Tag ))) ); fn is_kelvin(i: Partial<&str>) -> IResult, &str> { literal(Caseless("K")).parse_peek(i) } assert_eq!( is_kelvin(Partial::new("k")), Err(ErrMode::Backtrack(error_position!( &Partial::new("k"), ErrorKind::Tag ))) ); } #[test] fn partial_literal_fixed_size_array() { fn test(i: Partial<&[u8]>) -> IResult, &[u8]> { literal([0x42]).parse_peek(i) } fn test2(i: Partial<&[u8]>) -> IResult, &[u8]> { literal(&[0x42]).parse_peek(i) } let input = Partial::new(&[0x42, 0x00][..]); assert_eq!(test(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..]))); assert_eq!(test2(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..]))); } #[test] fn rest_on_slices() { let input: &[u8] = &b"Hello, world!"[..]; let empty: &[u8] = &b""[..]; assert_parse!(rest.parse_peek(input), Ok((empty, input))); } #[test] fn rest_on_strs() { let input: &str = "Hello, world!"; let empty: &str = ""; assert_parse!(rest.parse_peek(input), Ok((empty, input))); } #[test] fn rest_len_on_slices() { let input: &[u8] = &b"Hello, world!"[..]; assert_parse!(rest_len.parse_peek(input), Ok((input, input.len()))); }