winnow-0.5.15/.cargo_vcs_info.json0000644000000001360000000000100124450ustar { "git": { "sha1": "fa9fd560ea3cf0ae521bbd7e74d659d3697ab7fa" }, "path_in_vcs": "" }winnow-0.5.15/Cargo.lock0000644000001104370000000000100104260ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is-terminal", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "anstyle-wincon" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", "windows-sys 0.48.0", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" [[package]] name = "block-buffer" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytecount" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "circular" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea" [[package]] name = "clap" version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "cpufeatures" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "ctor" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn 1.0.109", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "errno" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", "windows-sys 0.48.0", ] [[package]] name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ "cc", "libc", ] [[package]] name = "escargot" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6" dependencies = [ "log", "once_cell", "serde", "serde_json", ] [[package]] name = "fastrand" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "generic-array" version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "handlebars" version = "4.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" dependencies = [ "log", "pest", "pest_derive", "serde", "serde_json", "thiserror", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", "windows-sys 0.48.0", ] [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.1", "rustix 0.38.6", "windows-sys 0.48.0", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lexopt" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libm" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "linux-raw-sys" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", "libm", ] [[package]] name = "num_cpus" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_pipe" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12" dependencies = [ "libc", "windows-sys 0.45.0", ] [[package]] name = "output_vt100" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] [[package]] name = "pest" version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" dependencies = [ "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" dependencies = [ "pest", "pest_generator", ] [[package]] name = "pest_generator" version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "pest_meta" version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" dependencies = [ "once_cell", "pest", "sha2", ] [[package]] name = "plotters" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_assertions" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" dependencies = [ "ctor", "diff", "output_vt100", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax 0.6.29", "rusty-fork", "tempfile", "unarray", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-xml" version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" dependencies = [ "memchr", ] [[package]] name = "quote" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core", ] [[package]] name = "rayon" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "num_cpus", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "regex-syntax 0.7.2", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "rustix" version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] [[package]] name = "rustix" version = "0.38.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f" dependencies = [ "bitflags 2.3.3", "errno", "libc", "linux-raw-sys 0.4.5", "windows-sys 0.48.0", ] [[package]] name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", "syn 2.0.22", ] [[package]] name = "serde_json" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "sha2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "similar" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" [[package]] name = "snapbox" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835" dependencies = [ "anstream", "anstyle", "escargot", "normalize-line-endings", "similar", "snapbox-macros", ] [[package]] name = "snapbox-macros" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31" dependencies = [ "anstream", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ "autocfg", "cfg-if", "fastrand", "redox_syscall", "rustix 0.37.20", "windows-sys 0.48.0", ] [[package]] name = "term-transcript" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d77280c9041d978e4ffedbdd19f710a1360085510c5e99dbcfec8e2f2bd4285" dependencies = [ "atty", "bytecount", "handlebars", "os_pipe", "pretty_assertions", "quick-xml", "serde", "termcolor", "unicode-width", ] [[package]] name = "termcolor" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" dependencies = [ "rustix 0.37.20", "windows-sys 0.48.0", ] [[package]] name = "thiserror" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "walkdir" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.22", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", "syn 2.0.22", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ "windows-targets 0.42.2", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.1", ] [[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", "windows_i686_gnu 0.48.0", "windows_i686_msvc 0.48.0", "windows_x86_64_gnu 0.48.0", "windows_x86_64_gnullvm 0.48.0", "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" version = "0.5.15" dependencies = [ "anstream", "anstyle", "circular", "criterion", "doc-comment", "escargot", "is-terminal", "lexopt", "memchr", "proptest", "snapbox", "term-transcript", "terminal_size", ] [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" winnow-0.5.15/Cargo.toml0000644000000102640000000000100104460ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.64.0" name = "winnow" version = "0.5.15" include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*", ] autoexamples = false description = "A byte-oriented, zero-copy, parser combinators library" readme = "README.md" keywords = [ "parser", "parser-combinators", "parsing", "streaming", "bit", ] categories = ["parsing"] license = "MIT" repository = "https://github.com/winnow-rs/winnow" [package.metadata.docs.rs] cargo-args = [ "-Zunstable-options", "-Zrustdoc-scrape-examples", ] features = ["unstable-doc"] rustdoc-args = [ "--cfg", "docsrs", ] [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{version}}" search = "Unreleased" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = "...{{tag_name}}" search = '\.\.\.HEAD' [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{date}}" search = "ReleaseDate" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ ## [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 = "" [profile.bench] lto = true codegen-units = 1 debug = 2 [[example]] name = "arithmetic" test = true required-features = ["alloc"] [[example]] name = "css" test = true required-features = ["alloc"] [[example]] name = "custom_error" test = true required-features = ["alloc"] [[example]] name = "http" required-features = ["alloc"] [[example]] name = "ini" test = true required-features = ["std"] [[example]] name = "json" test = true required-features = ["std"] [[example]] name = "ndjson" test = true required-features = ["std"] [[example]] name = "json_iterator" required-features = ["std"] [[example]] name = "iterator" [[example]] name = "s_expression" required-features = ["alloc"] [[example]] name = "string" required-features = ["alloc"] [[bench]] name = "arithmetic" path = "examples/arithmetic/bench.rs" harness = false [[bench]] name = "contains_token" harness = false [[bench]] name = "iter" harness = false [[bench]] name = "number" harness = false [[bench]] name = "http" path = "examples/http/bench.rs" harness = false required-features = ["alloc"] [[bench]] name = "ini" path = "examples/ini/bench.rs" harness = false required-features = ["std"] [[bench]] name = "json" path = "examples/json/bench.rs" harness = false required-features = ["std"] [dependencies.anstream] version = "0.3.2" optional = true [dependencies.anstyle] version = "1.0.1" optional = true [dependencies.is-terminal] version = "0.4.9" optional = true [dependencies.memchr] version = "2.5" optional = true default-features = false [dependencies.terminal_size] version = "0.2.6" optional = true [dev-dependencies.circular] version = "0.3.0" [dev-dependencies.criterion] version = "0.5.1" [dev-dependencies.doc-comment] version = "0.3" [dev-dependencies.escargot] version = "0.5.7" [dev-dependencies.lexopt] version = "0.3.0" [dev-dependencies.proptest] version = "1.2.0" [dev-dependencies.snapbox] version = "0.4.11" features = ["examples"] [dev-dependencies.term-transcript] version = "0.2.0" [features] alloc = [] debug = [ "dep:anstream", "dep:anstyle", "dep:is-terminal", "dep:terminal_size", ] default = ["std"] simd = ["dep:memchr"] std = [ "alloc", "memchr?/std", ] unstable-doc = [ "alloc", "std", "simd", ] winnow-0.5.15/Cargo.toml.orig000064400000000000000000000065451046102023000141360ustar 00000000000000[workspace] resolver = "2" members = ["fuzz"] [workspace.package] license = "MIT" edition = "2021" rust-version = "1.64.0" # MSRV include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*" ] [package] name = "winnow" version = "0.5.15" description = "A byte-oriented, zero-copy, parser combinators library" repository = "https://github.com/winnow-rs/winnow" categories = ["parsing"] keywords = ["parser", "parser-combinators", "parsing", "streaming", "bit"] autoexamples = false license.workspace = true edition.workspace = true rust-version.workspace = true include.workspace = true [package.metadata.docs.rs] features = ["unstable-doc"] rustdoc-args = ["--cfg", "docsrs"] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] [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}, ] [features] default = ["std"] alloc = [] std = ["alloc", "memchr?/std"] simd = ["dep:memchr"] debug = ["dep:anstream", "dep:anstyle", "dep:is-terminal", "dep:terminal_size"] unstable-doc = ["alloc", "std", "simd"] [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.2.6", 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" escargot = "0.5.7" snapbox = { version = "0.4.11", features = ["examples"] } circular = "0.3.0" [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 [[bench]] name = "contains_token" harness = false [[bench]] name = "iter" harness = false [[bench]] name = "number" harness = false [[bench]] name = "http" path = "examples/http/bench.rs" harness = false required-features = ["alloc"] [[bench]] name = "ini" path = "examples/ini/bench.rs" harness = false required-features = ["std"] [[bench]] name = "json" path = "examples/json/bench.rs" harness = false required-features = ["std"] winnow-0.5.15/LICENSE-MIT000064400000000000000000000017771046102023000127050ustar 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.5.15/README.md000064400000000000000000000025711046102023000125210ustar 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.5.15/benches/contains_token.rs000064400000000000000000000075321046102023000162370ustar 00000000000000use criterion::black_box; use winnow::combinator::alt; use winnow::combinator::repeat; use winnow::prelude::*; use winnow::token::take_till1; use winnow::token::take_while; fn contains_token(c: &mut criterion::Criterion) { let data = [ ("contiguous", CONTIGUOUS), ("interleaved", INTERLEAVED), ("canada", CANADA), ]; let mut group = c.benchmark_group("contains_token"); for (name, sample) in data { let len = sample.len(); group.throughput(criterion::Throughput::Bytes(len as u64)); group.bench_with_input(criterion::BenchmarkId::new("slice", name), &len, |b, _| { b.iter(|| black_box(parser_slice.parse_peek(black_box(sample)).unwrap())); }); group.bench_with_input(criterion::BenchmarkId::new("array", name), &len, |b, _| { b.iter(|| black_box(parser_array.parse_peek(black_box(sample)).unwrap())); }); group.bench_with_input(criterion::BenchmarkId::new("tuple", name), &len, |b, _| { b.iter(|| black_box(parser_tuple.parse_peek(black_box(sample)).unwrap())); }); group.bench_with_input( criterion::BenchmarkId::new("closure-or", name), &len, |b, _| { b.iter(|| black_box(parser_closure_or.parse_peek(black_box(sample)).unwrap())); }, ); group.bench_with_input( criterion::BenchmarkId::new("closure-matches", name), &len, |b, _| { b.iter(|| { black_box( parser_closure_matches .parse_peek(black_box(sample)) .unwrap(), ) }); }, ); } group.finish(); } fn parser_slice(input: &mut &str) -> PResult { let contains = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'][..]; repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) } fn parser_array(input: &mut &str) -> PResult { let contains = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) } fn parser_tuple(input: &mut &str) -> PResult { let contains = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) } fn parser_closure_or(input: &mut &str) -> PResult { let contains = |c: char| { c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9' }; repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) } fn parser_closure_matches(input: &mut &str) -> PResult { let contains = |c: char| matches!(c, '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'); repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input) } const CONTIGUOUS: &str = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; const INTERLEAVED: &str = "0123456789abc0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab"; const CANADA: &str = include_str!("../third_party/nativejson-benchmark/data/canada.json"); criterion::criterion_group!(benches, contains_token); criterion::criterion_main!(benches); winnow-0.5.15/benches/iter.rs000064400000000000000000000077371046102023000141730ustar 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)?; 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.5.15/benches/number.rs000064400000000000000000000034371046102023000145110ustar 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.5.15/examples/arithmetic/bench.rs000064400000000000000000000010331046102023000166260ustar 00000000000000mod parser; use winnow::prelude::*; use parser::expr; #[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));"; assert_eq!( expr.parse_peek(data), Ok((";", 2 * 2 / (5 - 1) + 3 * (1 + 2 * (45 / 2)),)) ); c.bench_function("arithmetic", |b| { b.iter(|| expr.parse_peek(data).unwrap()); }); } criterion::criterion_group!(benches, arithmetic); criterion::criterion_main!(benches); winnow-0.5.15/examples/arithmetic/main.rs000064400000000000000000000032561046102023000165040ustar 00000000000000use winnow::prelude::*; mod parser; mod parser_ast; fn main() -> Result<(), lexopt::Error> { let args = Args::parse()?; let input = args.input.as_deref().unwrap_or("1 + 1"); println!("{} =", input); match args.implementation { Impl::Eval => match parser::expr.parse(input) { Ok(result) => { println!(" {}", result); } Err(err) => { println!(" {}", err); } }, Impl::Ast => match parser_ast::expr.parse(input) { Ok(result) => { println!(" {:#?}", result); } Err(err) => { println!(" {}", err); } }, } Ok(()) } #[derive(Default)] struct Args { input: Option, implementation: Impl, } enum Impl { Eval, Ast, } 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), _ => Err("expected `eval`, `ast`"), })?; } Value(input) => { res.input = Some(input.string()?); } _ => return Err(arg.unexpected()), } } Ok(res) } } winnow-0.5.15/examples/arithmetic/parser.rs000064400000000000000000000051541046102023000170530ustar 00000000000000use std::str::FromStr; use winnow::prelude::*; use winnow::{ ascii::{digit1 as digits, space0 as spaces}, combinator::alt, combinator::delimited, combinator::fold_repeat, token::one_of, }; // Parser definition pub fn expr(i: &mut &str) -> PResult { let init = term.parse_next(i)?; fold_repeat( 0.., (one_of(['+', '-']), term), move || init, |acc, (op, val): (char, i64)| { if op == '+' { acc + val } else { acc - val } }, ) .parse_next(i) } // We read an initial factor and for each time we find // a * or / operator followed by another factor, we do // the math by folding everything fn term(i: &mut &str) -> PResult { let init = factor.parse_next(i)?; fold_repeat( 0.., (one_of(['*', '/']), factor), move || init, |acc, (op, val): (char, i64)| { if op == '*' { acc * val } else { acc / val } }, ) .parse_next(i) } // We transform an integer string into a i64, ignoring surrounding whitespaces // We look for a digit suite, and try to convert it. // If either str::from_utf8 or FromStr::from_str fail, // we fallback to the parens parser defined above fn factor(i: &mut &str) -> PResult { delimited( spaces, alt(( digits.try_map(FromStr::from_str), delimited('(', expr, ')'), parens, )), spaces, ) .parse_next(i) } // We parse any expr surrounded by parens, ignoring all whitespaces around those fn parens(i: &mut &str) -> PResult { delimited('(', expr, ')').parse_next(i) } #[test] fn factor_test() { assert_eq!(factor.parse_peek("3"), Ok(("", 3))); assert_eq!(factor.parse_peek(" 12"), Ok(("", 12))); assert_eq!(factor.parse_peek("537 "), Ok(("", 537))); assert_eq!(factor.parse_peek(" 24 "), Ok(("", 24))); } #[test] fn term_test() { assert_eq!(term.parse_peek(" 12 *2 / 3"), Ok(("", 8))); assert_eq!(term.parse_peek(" 2* 3 *2 *2 / 3"), Ok(("", 8))); assert_eq!(term.parse_peek(" 48 / 3/2"), Ok(("", 8))); } #[test] fn expr_test() { assert_eq!(expr.parse_peek(" 1 + 2 "), Ok(("", 3))); assert_eq!(expr.parse_peek(" 12 + 6 - 4+ 3"), Ok(("", 17))); assert_eq!(expr.parse_peek(" 1 + 2*3 + 4"), Ok(("", 11))); } #[test] fn parens_test() { assert_eq!(expr.parse_peek(" ( 2 )"), Ok(("", 2))); assert_eq!(expr.parse_peek(" 2* ( 3 + 4 ) "), Ok(("", 14))); assert_eq!(expr.parse_peek(" 2*2 / ( 5 - 1) + 3"), Ok(("", 4))); } winnow-0.5.15/examples/arithmetic/parser_ast.rs000064400000000000000000000103711046102023000177170ustar 00000000000000use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::str::FromStr; use winnow::prelude::*; use winnow::{ ascii::{digit1 as digit, multispace0 as multispace}, combinator::alt, combinator::repeat, combinator::{delimited, preceded}, }; #[derive(Debug)] pub enum Expr { Value(i64), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), Paren(Box), } #[derive(Debug)] pub enum Oper { Add, Sub, Mul, Div, } impl Display for Expr { fn fmt(&self, format: &mut Formatter<'_>) -> fmt::Result { use Expr::{Add, Div, Mul, Paren, Sub, Value}; match *self { Value(val) => write!(format, "{}", val), Add(ref left, ref right) => write!(format, "{} + {}", left, right), Sub(ref left, ref right) => write!(format, "{} - {}", left, right), Mul(ref left, ref right) => write!(format, "{} * {}", left, right), Div(ref left, ref right) => write!(format, "{} / {}", left, right), Paren(ref expr) => write!(format, "({})", expr), } } } pub fn expr(i: &mut &str) -> PResult { let initial = term(i)?; let remainder = repeat( 0.., alt(( |i: &mut &str| { let add = preceded("+", term).parse_next(i)?; Ok((Oper::Add, add)) }, |i: &mut &str| { let sub = preceded("-", term).parse_next(i)?; Ok((Oper::Sub, sub)) }, )), ) .parse_next(i)?; Ok(fold_exprs(initial, remainder)) } fn term(i: &mut &str) -> PResult { let initial = factor(i)?; let remainder = repeat( 0.., alt(( |i: &mut &str| { let mul = preceded("*", factor).parse_next(i)?; Ok((Oper::Mul, mul)) }, |i: &mut &str| { let div = preceded("/", factor).parse_next(i)?; Ok((Oper::Div, div)) }, )), ) .parse_next(i)?; Ok(fold_exprs(initial, remainder)) } fn factor(i: &mut &str) -> PResult { alt(( delimited(multispace, digit, multispace) .try_map(FromStr::from_str) .map(Expr::Value), parens, )) .parse_next(i) } fn parens(i: &mut &str) -> PResult { delimited( multispace, delimited("(", expr.map(|e| Expr::Paren(Box::new(e))), ")"), multispace, ) .parse_next(i) } fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr { remainder.into_iter().fold(initial, |acc, pair| { let (oper, expr) = pair; match oper { Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)), Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)), Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)), Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)), } }) } #[test] fn factor_test() { assert_eq!( factor .parse_peek(" 3 ") .map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("Value(3)"))) ); } #[test] fn term_test() { assert_eq!( term.parse_peek(" 3 * 5 ") .map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("Mul(Value(3), Value(5))"))) ); } #[test] fn expr_test() { assert_eq!( expr.parse_peek(" 1 + 2 * 3 ") .map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("Add(Value(1), Mul(Value(2), Value(3)))"))) ); assert_eq!( expr.parse_peek(" 1 + 2 * 3 / 4 - 5 ") .map(|(i, x)| (i, format!("{:?}", x))), Ok(( "", String::from("Sub(Add(Value(1), Div(Mul(Value(2), Value(3)), Value(4))), Value(5))") )) ); assert_eq!( expr.parse_peek(" 72 / 2 / 3 ") .map(|(i, x)| (i, format!("{:?}", x))), Ok(("", String::from("Div(Div(Value(72), Value(2)), Value(3))"))) ); } #[test] fn parens_test() { assert_eq!( expr.parse_peek(" ( 1 + 2 ) * 3 ") .map(|(i, x)| (i, format!("{:?}", x))), Ok(( "", String::from("Mul(Paren(Add(Value(1), Value(2))), Value(3))") )) ); } winnow-0.5.15/examples/css/main.rs000064400000000000000000000022571046102023000151430ustar 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.5.15/examples/css/parser.rs000064400000000000000000000014101046102023000155010ustar 00000000000000use winnow::prelude::*; use winnow::token::take_while; #[derive(Debug, Eq, PartialEq)] pub struct Color { pub red: u8, pub green: u8, pub blue: u8, } impl std::str::FromStr for Color { // The error must be owned type Err = String; fn from_str(s: &str) -> Result { hex_color.parse(s).map_err(|e| e.to_string()) } } pub fn hex_color(input: &mut &str) -> PResult { let _ = "#".parse_next(input)?; let (red, green, blue) = (hex_primary, hex_primary, hex_primary).parse_next(input)?; Ok(Color { red, green, blue }) } fn hex_primary(input: &mut &str) -> PResult { take_while(2, |c: char| c.is_ascii_hexdigit()) .try_map(|input| u8::from_str_radix(input, 16)) .parse_next(input) } winnow-0.5.15/examples/custom_error.rs000064400000000000000000000016061046102023000161470ustar 00000000000000use winnow::error::ErrMode; use winnow::error::ErrorKind; use winnow::error::ParserError; use winnow::prelude::*; #[derive(Debug, PartialEq, Eq)] pub enum CustomError { MyError, Nom(I, ErrorKind), } impl ParserError for CustomError { fn from_error_kind(input: &I, kind: ErrorKind) -> Self { CustomError::Nom(input.clone(), kind) } fn append(self, _: &I, _: ErrorKind) -> Self { self } } pub fn parse<'s>(_input: &mut &'s str) -> PResult<&'s str, CustomError<&'s str>> { Err(ErrMode::Backtrack(CustomError::MyError)) } fn main() {} #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let err = parse.parse_next(&mut "").unwrap_err(); match err { ErrMode::Backtrack(e) => assert_eq!(e, CustomError::MyError), _ => panic!("Unexpected error: {:?}", err), } } } winnow-0.5.15/examples/http/bench.rs000064400000000000000000000020211046102023000154520ustar 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.5.15/examples/http/main.rs000064400000000000000000000020761046102023000153310ustar 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.5.15/examples/http/parser.rs000064400000000000000000000063441046102023000157030ustar 00000000000000use winnow::prelude::*; use winnow::{ascii::line_ending, combinator::repeat, token::take_while}; pub type Stream<'i> = &'i [u8]; #[rustfmt::skip] #[derive(Debug)] #[allow(dead_code)] pub struct Request<'a> { method: &'a [u8], uri: &'a [u8], version: &'a [u8], } #[derive(Debug)] #[allow(dead_code)] pub struct Header<'a> { name: &'a [u8], value: Vec<&'a [u8]>, } pub fn parse(data: &[u8]) -> Option, Vec>)>> { 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> { let method = take_while(1.., is_token).parse_next(input)?; let _ = take_while(1.., is_space).parse_next(input)?; let uri = take_while(1.., is_not_space).parse_next(input)?; let _ = take_while(1.., is_space).parse_next(input)?; let version = http_version(input)?; let _ = line_ending.parse_next(input)?; Ok(Request { method, uri, version, }) } fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { let _ = "HTTP/".parse_next(input)?; let version = take_while(1.., is_version).parse_next(input)?; Ok(version) } fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { let _ = take_while(1.., is_horizontal_space).parse_next(input)?; let data = take_while(1.., not_line_ending).parse_next(input)?; let _ = line_ending.parse_next(input)?; Ok(data) } fn message_header<'s>(input: &mut Stream<'s>) -> PResult> { let name = take_while(1.., is_token).parse_next(input)?; let _ = ':'.parse_next(input)?; let value = repeat(1.., message_header_value).parse_next(input)?; Ok(Header { name, value }) } #[rustfmt::skip] #[allow(clippy::match_same_arms)] #[allow(clippy::match_like_matches_macro)] fn is_token(c: u8) -> bool { match c { 128..=255 => false, 0..=31 => false, b'(' => false, b')' => false, b'<' => false, b'>' => false, b'@' => false, b',' => false, b';' => false, b':' => false, b'\\' => false, b'"' => false, b'/' => false, b'[' => false, b']' => false, b'?' => false, b'=' => false, b'{' => false, b'}' => false, b' ' => false, _ => true, } } fn is_version(c: u8) -> bool { (b'0'..=b'9').contains(&c) || c == b'.' } fn not_line_ending(c: u8) -> bool { c != b'\r' && c != b'\n' } fn is_space(c: u8) -> bool { c == b' ' } fn is_not_space(c: u8) -> bool { c != b' ' } fn is_horizontal_space(c: u8) -> bool { c == b' ' || c == b'\t' } winnow-0.5.15/examples/http/parser_streaming.rs000064400000000000000000000064071046102023000177540ustar 00000000000000use winnow::{ ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while, }; pub type Stream<'i> = Partial<&'i [u8]>; #[rustfmt::skip] #[derive(Debug)] #[allow(dead_code)] pub struct Request<'a> { method: &'a [u8], uri: &'a [u8], version: &'a [u8], } #[derive(Debug)] #[allow(dead_code)] pub struct Header<'a> { name: &'a [u8], value: Vec<&'a [u8]>, } pub fn parse(data: &[u8]) -> Option, Vec>)>> { 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> { let method = take_while(1.., is_token).parse_next(input)?; let _ = take_while(1.., is_space).parse_next(input)?; let uri = take_while(1.., is_not_space).parse_next(input)?; let _ = take_while(1.., is_space).parse_next(input)?; let version = http_version(input)?; let _ = line_ending.parse_next(input)?; Ok(Request { method, uri, version, }) } fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { let _ = "HTTP/".parse_next(input)?; let version = take_while(1.., is_version).parse_next(input)?; Ok(version) } fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> { let _ = take_while(1.., is_horizontal_space).parse_next(input)?; let data = take_while(1.., not_line_ending).parse_next(input)?; let _ = line_ending.parse_next(input)?; Ok(data) } fn message_header<'s>(input: &mut Stream<'s>) -> PResult> { let name = take_while(1.., is_token).parse_next(input)?; let _ = ':'.parse_next(input)?; let value = repeat(1.., message_header_value).parse_next(input)?; Ok(Header { name, value }) } #[rustfmt::skip] #[allow(clippy::match_same_arms)] #[allow(clippy::match_like_matches_macro)] fn is_token(c: u8) -> bool { match c { 128..=255 => false, 0..=31 => false, b'(' => false, b')' => false, b'<' => false, b'>' => false, b'@' => false, b',' => false, b';' => false, b':' => false, b'\\' => false, b'"' => false, b'/' => false, b'[' => false, b']' => false, b'?' => false, b'=' => false, b'{' => false, b'}' => false, b' ' => false, _ => true, } } fn is_version(c: u8) -> bool { (b'0'..=b'9').contains(&c) || c == b'.' } fn not_line_ending(c: u8) -> bool { c != b'\r' && c != b'\n' } fn is_space(c: u8) -> bool { c == b' ' } fn is_not_space(c: u8) -> bool { c != b' ' } fn is_horizontal_space(c: u8) -> bool { c == b' ' || c == b'\t' } winnow-0.5.15/examples/ini/bench.rs000064400000000000000000000033661046102023000152670ustar 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.5.15/examples/ini/main.rs000064400000000000000000000024711046102023000151300ustar 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.5.15/examples/ini/parser.rs000064400000000000000000000076131046102023000155030ustar 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 type Stream<'i> = &'i [u8]; pub 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 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.5.15/examples/ini/parser_str.rs000064400000000000000000000125601046102023000163700ustar 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_till0, take_while}, }; pub type Stream<'i> = &'i str; pub 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_till0(is_line_ending_or_comment).parse_next(i)?; let _ = opt(space).parse_next(i)?; let _ = opt((";", not_line_ending)).parse_next(i)?; let _ = opt(space_or_line_ending).parse_next(i)?; Ok((key, val)) } fn is_line_ending_or_comment(chr: char) -> bool { chr == ';' || chr == '\n' } fn not_line_ending<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { take_while(0.., |c| c != '\r' && c != '\n').parse_next(i) } fn space_or_line_ending<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { take_while(1.., [' ', '\r', '\n']).parse_next(i) } #[test] fn parse_category_test() { let ini_file = "[category] parameter=value key = value2"; let ini_without_category = "parameter=value key = value2"; let res = category.parse_peek(ini_file); println!("{:?}", res); match res { Ok((i, o)) => println!("i: {} | o: {:?}", i, o), _ => println!("error"), } assert_eq!(res, Ok((ini_without_category, "category"))); } #[test] fn parse_key_value_test() { let ini_file = "parameter=value key = value2"; let ini_without_key_value = "key = value2"; let res = key_value.parse_peek(ini_file); println!("{:?}", res); match res { Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), _ => println!("error"), } assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); } #[test] fn parse_key_value_with_space_test() { let ini_file = "parameter = value key = value2"; let ini_without_key_value = "key = value2"; let res = key_value.parse_peek(ini_file); println!("{:?}", res); match res { Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), _ => println!("error"), } assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); } #[test] fn parse_key_value_with_comment_test() { let ini_file = "parameter=value;abc key = value2"; let ini_without_key_value = "key = value2"; let res = key_value.parse_peek(ini_file); println!("{:?}", res); match res { Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2), _ => println!("error"), } assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value")))); } #[test] fn parse_multiple_keys_and_values_test() { let ini_file = "parameter=value;abc key = value2 [category]"; let ini_without_key_value = "[category]"; let res = keys_and_values.parse_peek(ini_file); println!("{:?}", res); match res { Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), _ => println!("error"), } let mut expected: HashMap<&str, &str> = HashMap::new(); expected.insert("parameter", "value"); expected.insert("key", "value2"); assert_eq!(res, Ok((ini_without_key_value, expected))); } #[test] fn parse_category_then_multiple_keys_and_values_test() { //FIXME: there can be an empty line or a comment line after a category let ini_file = "[abcd] parameter=value;abc key = value2 [category]"; let ini_after_parser = "[category]"; let res = category_and_keys.parse_peek(ini_file); println!("{:?}", res); match res { Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), _ => println!("error"), } let mut expected_h: HashMap<&str, &str> = HashMap::new(); expected_h.insert("parameter", "value"); expected_h.insert("key", "value2"); assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h)))); } #[test] fn parse_multiple_categories_test() { let ini_file = "[abcd] parameter=value;abc key = value2 [category] parameter3=value3 key4 = value4 "; let res = categories.parse_peek(ini_file); //println!("{:?}", res); match res { Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o), _ => println!("error"), } let mut expected_1: HashMap<&str, &str> = HashMap::new(); expected_1.insert("parameter", "value"); expected_1.insert("key", "value2"); let mut expected_2: HashMap<&str, &str> = HashMap::new(); expected_2.insert("parameter3", "value3"); expected_2.insert("key4", "value4"); let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new(); expected_h.insert("abcd", expected_1); expected_h.insert("category", expected_2); assert_eq!(res, Ok(("", expected_h))); } winnow-0.5.15/examples/iterator.rs000064400000000000000000000046031046102023000152550ustar 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 {:?}, remaining input is '{}'", res, remaining_input ); } winnow-0.5.15/examples/json/bench.rs000064400000000000000000000043731046102023000154600ustar 00000000000000use winnow::prelude::*; use winnow::Partial; mod json; mod parser; mod parser_dispatch; mod parser_partial; fn json_bench(c: &mut criterion::Criterion) { let data = [("small", SMALL), ("canada", CANADA)]; let mut group = c.benchmark_group("json"); for (name, sample) in data { let len = sample.len(); group.throughput(criterion::Throughput::Bytes(len as u64)); group.bench_with_input(criterion::BenchmarkId::new("basic", name), &len, |b, _| { type Error<'i> = winnow::error::InputError>; b.iter(|| parser::json::.parse_peek(sample).unwrap()); }); group.bench_with_input(criterion::BenchmarkId::new("unit", name), &len, |b, _| { type Error<'i> = (); b.iter(|| parser::json::.parse_peek(sample).unwrap()); }); group.bench_with_input( criterion::BenchmarkId::new("context", name), &len, |b, _| { type Error<'i> = winnow::error::ContextError>; b.iter(|| parser::json::.parse_peek(sample).unwrap()); }, ); group.bench_with_input( criterion::BenchmarkId::new("dispatch", name), &len, |b, _| { type Error<'i> = winnow::error::InputError>; b.iter(|| parser_dispatch::json::.parse_peek(sample).unwrap()); }, ); group.bench_with_input( criterion::BenchmarkId::new("streaming", name), &len, |b, _| { type Error<'i> = winnow::error::InputError>; 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.5.15/examples/json/json.rs000064400000000000000000000003311046102023000153400ustar 00000000000000use std::collections::HashMap; #[derive(Debug, PartialEq, Clone)] pub enum JsonValue { Null, Boolean(bool), Str(String), Num(f64), Array(Vec), Object(HashMap), } winnow-0.5.15/examples/json/main.rs000064400000000000000000000043641046102023000153250ustar 00000000000000mod json; mod parser; mod parser_dispatch; #[allow(dead_code)] mod parser_partial; use winnow::error::ErrorKind; use winnow::prelude::*; fn main() -> Result<(), lexopt::Error> { let args = Args::parse()?; let data = args.input.as_deref().unwrap_or(if args.invalid { " { \"a\"\t: 42, \"b\": [ \"x\", \"y\", 12 ] , \"c\": { 1\"hello\" : \"world\" } } " } else { " { \"a\"\t: 42, \"b\": [ \"x\", \"y\", 12 ] , \"c\": { \"hello\" : \"world\" } } " }); let result = match args.implementation { Impl::Naive => parser::json::.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.5.15/examples/json/parser.rs000064400000000000000000000256621046102023000157010ustar 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::{fold_repeat, separated0}, error::{AddContext, ParserError}, token::{any, none_of, take, take_while}, }; use crate::json::JsonValue; pub type Stream<'i> = &'i str; /// The root element of a JSON parser is any value /// /// A parser has the following signature: /// `&mut Stream -> PResult`, 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 fn json<'i, E: ParserError> + AddContext, &'static str>>( 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, &'static str>>( 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) } /// `tag(string)` generates a parser that recognizes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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 recognize the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, &'static str>>( 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( fold_repeat(0.., character, String::new, |mut string, c| { string.push(c); string }), '\"', )), ) // `context` lets you add a static string to errors to provide more information in the // error chain (to indicate which parser had an error) .context("string") .parse_next(input) } /// You can mix the above declarative parsing with an imperative style to handle more unique cases, /// like escaping fn character<'i, E: ParserError>>(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 `separated0` or `many0`, will call a parser repeatedly, /// accumulating results in a `Vec`, until it encounters an error. /// If you want more control on the parser application, check out the `iterator` /// combinator (cf `examples/iterator.rs`) fn array<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), ) .context("array") .parse_next(input) } fn object<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), ) .context("object") .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult<(String, JsonValue), E> { separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) } /// Parser combinators are constructed from the bottom up: /// first we write parsers for the smallest elements (here a space character), /// then we'll combine them in larger parsers fn ws<'i, E: ParserError>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { // Combinators like `take_while` return a function. That function is the // parser,to which we can pass the input take_while(0.., WS).parse_next(input) } const WS: &[char] = &[' ', '\t', '\r', '\n']; #[cfg(test)] mod test { #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error<'i> = winnow::error::InputError<&'i str>; #[test] fn json_string() { assert_eq!( string::>.parse_peek("\"\""), Ok(("", "".to_string())) ); assert_eq!( string::>.parse_peek("\"abc\""), Ok(("", "abc".to_string())) ); 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_string())), ); assert_eq!( string::>.parse_peek("\"\\uD83D\\uDE10\""), Ok(("", "😐".to_string())) ); 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_string(), Num(42.0)), ("b".to_string(), Str("x".to_string())), ] .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_string())]); 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_string(), Null), ("true".to_string(), Boolean(true)), ("false".to_string(), Boolean(false)), ("number".to_string(), Num(123e4)), ("string".to_string(), Str(" abc 123 ".to_string())), ( "array".to_string(), Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) ), ( "object".to_string(), Object( vec![ ("a".to_string(), Num(1.0)), ("b".to_string(), Str("c".to_string())), ] .into_iter() .collect() ) ), ("empty_array".to_string(), Array(vec![]),), ("empty_object".to_string(), Object(HashMap::new()),), ] .into_iter() .collect() ) )) ); } } winnow-0.5.15/examples/json/parser_dispatch.rs000064400000000000000000000264441046102023000175570ustar 00000000000000use std::collections::HashMap; use std::str; use winnow::prelude::*; use winnow::{ ascii::float, combinator::cut_err, combinator::fail, combinator::peek, combinator::success, combinator::{alt, dispatch}, combinator::{delimited, preceded, separated_pair, terminated}, combinator::{fold_repeat, separated0}, error::{AddContext, ParserError}, token::{any, none_of, take, take_while}, }; use crate::json::JsonValue; pub type Stream<'i> = &'i str; /// The root element of a JSON parser is any value /// /// A parser has the following signature: /// `&mut Stream -> PResult`, 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 fn json<'i, E: ParserError> + AddContext, &'static str>>( 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, &'static str>>( 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) } /// `tag(string)` generates a parser that recognizes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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 recognize the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, &'static str>>( 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( fold_repeat(0.., character, String::new, |mut string, c| { string.push(c); string }), '\"', )), ) // `context` lets you add a static string to errors to provide more information in the // error chain (to indicate which parser had an error) .context("string") .parse_next(input) } /// You can mix the above declarative parsing with an imperative style to handle more unique cases, /// like escaping fn character<'i, E: ParserError>>(input: &mut Stream<'i>) -> PResult { let c = none_of('\"').parse_next(input)?; if c == '\\' { dispatch!(any; '"' => success('"'), '\\' => success('\\'), '/' => success('/'), 'b' => success('\x08'), 'f' => success('\x0C'), 'n' => success('\n'), 'r' => success('\r'), 't' => success('\t'), 'u' => unicode_escape, _ => fail, ) .parse_next(input) } else { Ok(c) } } fn unicode_escape<'i, E: ParserError>>(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 `separated0` or `many0`, will call a parser repeatedly, /// accumulating results in a `Vec`, until it encounters an error. /// If you want more control on the parser application, check out the `iterator` /// combinator (cf `examples/iterator.rs`) fn array<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), ) .context("array") .parse_next(input) } fn object<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), ) .context("object") .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult<(String, JsonValue), E> { separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) } /// Parser combinators are constructed from the bottom up: /// first we write parsers for the smallest elements (here a space character), /// then we'll combine them in larger parsers fn ws<'i, E: ParserError>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { // Combinators like `take_while` return a function. That function is the // parser,to which we can pass the input take_while(0.., WS).parse_next(input) } const WS: &[char] = &[' ', '\t', '\r', '\n']; #[cfg(test)] mod test { #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error<'i> = winnow::error::InputError<&'i str>; #[test] fn json_string() { assert_eq!( string::>.parse_peek("\"\""), Ok(("", "".to_string())) ); assert_eq!( string::>.parse_peek("\"abc\""), Ok(("", "abc".to_string())) ); 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_string())), ); assert_eq!( string::>.parse_peek("\"\\uD83D\\uDE10\""), Ok(("", "😐".to_string())) ); 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_string(), Num(42.0)), ("b".to_string(), Str("x".to_string())), ] .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_string())]); 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_string(), Null), ("true".to_string(), Boolean(true)), ("false".to_string(), Boolean(false)), ("number".to_string(), Num(123e4)), ("string".to_string(), Str(" abc 123 ".to_string())), ( "array".to_string(), Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) ), ( "object".to_string(), Object( vec![ ("a".to_string(), Num(1.0)), ("b".to_string(), Str("c".to_string())), ] .into_iter() .collect() ) ), ("empty_array".to_string(), Array(vec![]),), ("empty_object".to_string(), Object(HashMap::new()),), ] .into_iter() .collect() ) )) ); } } winnow-0.5.15/examples/json/parser_partial.rs000064400000000000000000000273101046102023000174050ustar 00000000000000use std::collections::HashMap; use std::str; use winnow::prelude::*; use winnow::{ ascii::float, combinator::alt, combinator::{cut_err, rest}, combinator::{delimited, preceded, separated_pair, terminated}, combinator::{fold_repeat, separated0}, error::{AddContext, ParserError}, stream::Partial, token::{any, none_of, take, take_while}, }; use crate::json::JsonValue; pub type Stream<'i> = Partial<&'i str>; /// The root element of a JSON parser is any value /// /// A parser has the following signature: /// `&mut Stream -> PResult`, 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 fn json<'i, E: ParserError> + AddContext, &'static str>>( 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, &'static str>>( 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) } /// `tag(string)` generates a parser that recognizes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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 recognize the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, &'static str>>( 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( fold_repeat(0.., character, String::new, |mut string, c| { string.push(c); string }), '\"', )), ) // `context` lets you add a static string to errors to provide more information in the // error chain (to indicate which parser had an error) .context("string") .parse_next(input) } /// You can mix the above declarative parsing with an imperative style to handle more unique cases, /// like escaping fn character<'i, E: ParserError>>(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 `separated0` or `many0`, will call a parser repeatedly, /// accumulating results in a `Vec`, until it encounters an error. /// If you want more control on the parser application, check out the `iterator` /// combinator (cf `examples/iterator.rs`) fn array<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), ) .context("array") .parse_next(input) } fn object<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), ) .context("object") .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult<(String, JsonValue), E> { separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) } /// Parser combinators are constructed from the bottom up: /// first we write parsers for the smallest elements (here a space character), /// then we'll combine them in larger parsers fn ws<'i, E: ParserError>>(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(dead_code)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error<'i> = winnow::error::InputError>; #[test] fn json_string() { assert_eq!( string::>.parse_peek(Partial::new("\"\"")), Ok((Partial::new(""), "".to_string())) ); assert_eq!( string::>.parse_peek(Partial::new("\"abc\"")), Ok((Partial::new(""), "abc".to_string())) ); 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_string() )), ); assert_eq!( string::>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")), Ok((Partial::new(""), "😐".to_string())) ); 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_string(), Num(42.0)), ("b".to_string(), Str("x".to_string())), ] .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_string())]); 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_string(), Null), ("true".to_string(), Boolean(true)), ("false".to_string(), Boolean(false)), ("number".to_string(), Num(123e4)), ("string".to_string(), Str(" abc 123 ".to_string())), ( "array".to_string(), Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) ), ( "object".to_string(), Object( vec![ ("a".to_string(), Num(1.0)), ("b".to_string(), Str("c".to_string())), ] .into_iter() .collect() ) ), ("empty_array".to_string(), Array(vec![]),), ("empty_object".to_string(), Object(HashMap::new()),), ] .into_iter() .collect() ) )) ); } } winnow-0.5.15/examples/json_iterator.rs000064400000000000000000000224051046102023000163060ustar 00000000000000use std::collections::HashMap; use winnow::prelude::*; use winnow::{ ascii::{alphanumeric1 as alphanumeric, escaped, float}, combinator::alt, combinator::cut_err, combinator::separated0, combinator::{preceded, separated_pair, terminated}, error::ParserError, error::StrContext, stream::Offset, token::one_of, token::{tag, take_while}, }; use std::cell::Cell; use std::str; #[derive(Clone, Debug)] pub struct JsonValue<'a, 'b> { input: &'a str, pub offset: &'b Cell, } 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 tag::<_, _, ()>("[").parse_next(&mut data) { Err(_) => None, Ok(_) => { println!("["); self.offset(data); let mut first = true; let mut done = false; let mut previous = std::usize::MAX; let v = self.clone(); Some(std::iter::from_fn(move || { if done { return None; } // if we ignored one of the items, skip over the value if v.offset.get() == previous { println!("skipping value"); if value(&mut data).is_ok() { v.offset(data); } } if tag::<_, _, ()>("]").parse_next(&mut data).is_ok() { println!("]"); v.offset(data); done = true; return None; } if first { first = false; } else { match tag::<_, _, ()>(",").parse_next(&mut data) { Ok(_) => { println!(","); v.offset(data); } Err(_) => { done = true; return None; } } } println!("-> {}", v.data()); previous = v.offset.get(); Some(v.clone()) })) } } } pub fn object(&self) -> Option)>> { println!("object()"); let mut data = self.data(); match tag::<_, _, ()>("{").parse_next(&mut data) { Err(_) => None, Ok(_) => { self.offset(data); println!("{{"); let mut first = true; let mut done = false; let mut previous = std::usize::MAX; let v = self.clone(); Some(std::iter::from_fn(move || { if done { return None; } // if we ignored one of the items, skip over the value if v.offset.get() == previous { println!("skipping value"); if value(&mut data).is_ok() { v.offset(data); } } if tag::<_, _, ()>("}").parse_next(&mut data).is_ok() { println!("}}"); v.offset(data); done = true; return None; } if first { first = false; } else { match tag::<_, _, ()>(",").parse_next(&mut data) { Ok(_) => { println!(","); v.offset(data); } Err(_) => { done = true; return None; } } } match string(&mut data) { Ok(key) => { v.offset(data); match tag::<_, _, ()>(":").parse_next(&mut data) { Err(_) => None, Ok(_) => { v.offset(data); previous = v.offset.get(); println!("-> {} => {}", key, v.data()); Some((key, v.clone())) } } } _ => None, } })) } } } } fn sp<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> { let chars = " \t\r\n"; take_while(0.., move |c| chars.contains(c)).parse_next(i) } fn parse_str<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> { escaped(alphanumeric, '\\', one_of(['"', 'n', '\\'])).parse_next(i) } fn string<'s>(i: &mut &'s str) -> PResult<&'s str> { preceded('\"', cut_err(terminated(parse_str, '\"'))) .context(StrContext::Label("string")) .parse_next(i) } fn boolean(input: &mut &str) -> PResult { alt(("false".map(|_| false), "true".map(|_| true))).parse_next(input) } fn array(i: &mut &str) -> PResult<()> { preceded( '[', cut_err(terminated( separated0(value, preceded(sp, ',')), preceded(sp, ']'), )), ) .context(StrContext::Label("array")) .parse_next(i) } fn key_value<'s>(i: &mut &'s str) -> PResult<(&'s str, ())> { separated_pair(preceded(sp, string), cut_err(preceded(sp, ':')), value).parse_next(i) } fn hash(i: &mut &str) -> PResult<()> { preceded( '{', cut_err(terminated( separated0(key_value, preceded(sp, ',')), preceded(sp, '}'), )), ) .context(StrContext::Label("map")) .parse_next(i) } fn value(i: &mut &str) -> PResult<()> { preceded( sp, alt(( hash, array, string.map(|_| ()), float::<_, f64, _>.map(|_| ()), boolean.map(|_| ()), )), ) .parse_next(i) } /// object(input) -> iterator over (key, `JsonValue`) /// array(input) -> iterator over `JsonValue` /// /// JsonValue.string -> iterator over String (returns None after first successful call) /// /// object(input).filter(|(k, _)| k == "users").flatten(|(_, v)| v.object()).filter(|(k, _)| k == "city").flatten(|(_,v)| v.string()) fn main() { /*let data = "{ \"users\": { \"user1\" : { \"city\": \"Nantes\", \"country\": \"France\" }, \"user2\" : { \"city\": \"Bruxelles\", \"country\": \"Belgium\" }, \"user3\": { \"city\": \"Paris\", \"country\": \"France\", \"age\": 30 } }, \"countries\": [\"France\", \"Belgium\"] }"; */ let data = "{\"users\":{\"user1\":{\"city\":\"Nantes\",\"country\":\"France\"},\"user2\":{\"city\":\"Bruxelles\",\"country\":\"Belgium\"},\"user3\":{\"city\":\"Paris\",\"country\":\"France\",\"age\":30}},\"countries\":[\"France\",\"Belgium\"]}"; let offset = Cell::new(0); { let parser = JsonValue::new(data, &offset); if let Some(o) = parser.object() { let s: HashMap<&str, &str> = o .filter(|(k, _)| *k == "users") .filter_map(|(_, v)| v.object()) .flatten() .filter_map(|(user, v)| v.object().map(|o| (user, o))) .flat_map(|(user, o)| { o.filter(|(k, _)| *k == "city") .filter_map(move |(_, v)| v.string().map(|s| (user, s))) }) .collect(); println!("res = {:?}", s); } }; } winnow-0.5.15/examples/ndjson/example.ndjson000064400000000000000000003126521046102023000172270ustar 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.5.15/examples/ndjson/main.rs000064400000000000000000000076401046102023000156470ustar 00000000000000mod parser; use std::io::Read; use winnow::error::ErrMode; use winnow::error::InputError; use winnow::error::Needed; use winnow::prelude::*; use winnow::stream::Offset; fn main() -> Result<(), lexopt::Error> { let args = Args::parse()?; let input = args.input.ok_or_else(|| lexopt::Error::MissingValue { option: Some("".to_owned()), })?; let mut file = std::fs::File::open(&input).map_err(to_lexopt)?; // Intentionally starting with a small buffer to make it easier to show `Incomplete` handling let buffer_size = 10; let min_buffer_growth = 100; let buffer_growth_factor = 2; let mut buffer = circular::Buffer::with_capacity(buffer_size); loop { let read = file.read(buffer.space()).map_err(to_lexopt)?; eprintln!("read {}", read); if read == 0 { // Should be EOF since we always make sure there is `available_space` assert_ne!(buffer.available_space(), 0); assert_eq!( buffer.available_data(), 0, "leftover data: {}", String::from_utf8_lossy(buffer.data()) ); break; } buffer.fill(read); loop { let input = parser::Stream::new(std::str::from_utf8(buffer.data()).map_err(to_lexopt)?); match parser::ndjson::>.parse_peek(input) { Ok((remainder, value)) => { println!("{:?}", value); println!(); // Tell the buffer how much we read let consumed = remainder.offset_from(&input); buffer.consume(consumed); } Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => { return Err(fmt_lexopt(e.to_string())); } Err(ErrMode::Incomplete(Needed::Size(size))) => { // Without the format telling us how much space is required, we really should // treat this the same as `Unknown` but are doing this to demonstrate how to // handle `Size`. // // Even when the format has a header to tell us `Size`, we could hit incidental // `Size(1)`s, so make sure we buffer more space than that to avoid reading // one byte at a time let head_room = size.get().max(min_buffer_growth); let new_capacity = buffer.available_data() + head_room; eprintln!("growing buffer to {}", new_capacity); buffer.grow(new_capacity); if buffer.available_space() < head_room { eprintln!("buffer shift"); buffer.shift(); } break; } Err(ErrMode::Incomplete(Needed::Unknown)) => { let new_capacity = buffer_growth_factor * buffer.capacity(); eprintln!("growing buffer to {}", new_capacity); buffer.grow(new_capacity); break; } } } } Ok(()) } #[derive(Default)] struct Args { input: Option, } 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.5.15/examples/ndjson/parser.rs000064400000000000000000000266051046102023000162210ustar 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::{fold_repeat, separated0}, error::{AddContext, ParserError}, stream::Partial, token::{any, none_of, take, take_while}, }; #[derive(Debug, PartialEq, Clone)] pub enum JsonValue { Null, Boolean(bool), Str(String), Num(f64), Array(Vec), Object(HashMap), } /// Use `Partial` to cause `ErrMode::Incomplete` while parsing pub type Stream<'i> = Partial<&'i str>; pub fn ndjson<'i, E: ParserError> + AddContext, &'static str>>( 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, &'static str>>( 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) } /// `tag(string)` generates a parser that recognizes the argument string. /// /// This also shows returning a sub-slice of the original input fn null<'i, E: ParserError>>(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 recognize the double quote /// character, before the string (using `preceded`) and after the string (using `terminated`). fn string<'i, E: ParserError> + AddContext, &'static str>>( 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( fold_repeat(0.., character, String::new, |mut string, c| { string.push(c); string }), '\"', )), ) // `context` lets you add a static string to errors to provide more information in the // error chain (to indicate which parser had an error) .context("string") .parse_next(input) } /// You can mix the above declarative parsing with an imperative style to handle more unique cases, /// like escaping fn character<'i, E: ParserError>>(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 `separated0` or `many0`, will call a parser repeatedly, /// accumulating results in a `Vec`, until it encounters an error. /// If you want more control on the parser application, check out the `iterator` /// combinator (cf `examples/iterator.rs`) fn array<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('[', ws), cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))), ) .context("array") .parse_next(input) } fn object<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult, E> { preceded( ('{', ws), cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))), ) .context("object") .parse_next(input) } fn key_value<'i, E: ParserError> + AddContext, &'static str>>( input: &mut Stream<'i>, ) -> PResult<(String, JsonValue), E> { separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input) } /// Parser combinators are constructed from the bottom up: /// first we write parsers for the smallest elements (here a space character), /// then we'll combine them in larger parsers fn ws<'i, E: ParserError>>(input: &mut Stream<'i>) -> PResult<&'i str, E> { // Combinators like `take_while` return a function. That function is the // parser,to which we can pass the input take_while(0.., WS).parse_next(input) } const WS: &[char] = &[' ', '\t']; #[cfg(test)] mod test { #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches use super::*; #[allow(clippy::useless_attribute)] #[allow(dead_code)] // its dead for benches type Error<'i> = winnow::error::InputError>; #[test] fn json_string() { assert_eq!( string::>.parse_peek(Partial::new("\"\"")), Ok((Partial::new(""), "".to_string())) ); assert_eq!( string::>.parse_peek(Partial::new("\"abc\"")), Ok((Partial::new(""), "abc".to_string())) ); 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_string() )), ); assert_eq!( string::>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")), Ok((Partial::new(""), "😐".to_string())) ); 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_string(), Num(42.0)), ("b".to_string(), Str("x".to_string())), ] .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_string())]); 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_string(), Null), ("true".to_string(), Boolean(true)), ("false".to_string(), Boolean(false)), ("number".to_string(), Num(123e4)), ("string".to_string(), Str(" abc 123 ".to_string())), ( "array".to_string(), Array(vec![Boolean(false), Num(1.0), Str("two".to_string())]) ), ( "object".to_string(), Object( vec![ ("a".to_string(), Num(1.0)), ("b".to_string(), Str("c".to_string())), ] .into_iter() .collect() ) ), ("empty_array".to_string(), Array(vec![]),), ("empty_object".to_string(), Object(HashMap::new()),), ] .into_iter() .collect() )) )) ); } } winnow-0.5.15/examples/s_expression/main.rs000064400000000000000000000010631046102023000170660ustar 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.5.15/examples/s_expression/parser.rs000064400000000000000000000324711046102023000174450ustar 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 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_string())) } /// For parsing, we start by defining the types that define the shape of data that we want. /// In this case, we want something tree-like /// The remaining half is Lists. We implement these as recursive Expressions. /// For a list of numbers, we have `'(1 2 3)`, which we'll parse to: /// ``` /// Expr::Quote(vec![Expr::Constant(Atom::Num(1)), /// Expr::Constant(Atom::Num(2)), /// Expr::Constant(Atom::Num(3))]) /// Quote takes an S-expression and prevents evaluation of it, making it a data /// structure that we can deal with programmatically. Thus any valid expression /// is also a valid data structure in Lisp itself. #[derive(Debug, Eq, PartialEq, Clone)] pub enum Expr { Constant(Atom), /// (func-name arg1 arg2) Application(Box, 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 enum Atom { Num(i32), Keyword(String), Boolean(bool), BuiltIn(BuiltIn), } /// Now, the most basic type. We define some built-in functions that our lisp has #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum BuiltIn { Plus, Minus, Times, Divide, Equal, Not, } /// With types defined, we move onto the top-level expression parser! fn parse_expr(i: &mut &'_ str) -> PResult { 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_string())) .parse_next(i) } /// We can now use our new combinator to define the rest of the `Expr`s. /// /// Starting with function application, we can see how the parser mirrors our data /// definitions: our definition is `Application(Box, 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.5.15/examples/string/main.rs000064400000000000000000000036701046102023000156610ustar 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.5.15/examples/string/parser.rs000064400000000000000000000154231046102023000162300ustar 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::fold_repeat; use winnow::combinator::{delimited, preceded}; use winnow::error::{FromExternalError, ParserError}; use winnow::prelude::*; use winnow::token::{take_till1, take_while}; /// Parse a string. Use a loop of `parse_fragment` and push all of the fragments /// into an output string. pub fn parse_string<'a, E>(input: &mut &'a str) -> PResult where E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, { // fold_repeat is the equivalent of iterator::fold. It runs a parser in a loop, // and for each output value, calls a folding function on each output value. let build_string = fold_repeat( 0.., // Our parser function – parses a single string fragment parse_fragment, // Our init value, an empty string String::new, // Our folding function. For each fragment, append the fragment to the // string. |mut string, fragment| { match fragment { StringFragment::Literal(s) => string.push_str(s), StringFragment::EscapedChar(c) => string.push(c), StringFragment::EscapedWS => {} } string }, ); // Finally, parse the string. Note that, if `build_string` could accept a raw // " character, the closing delimiter " would never match. When using // `delimited` with a looping parser (like fold_repeat), be sure that the // loop won't accidentally match your closing delimiter! delimited('"', build_string, '"').parse_next(input) } /// A string fragment contains a fragment of a string being parsed: either /// a non-empty Literal (a series of non-escaped characters), a single /// parsed escaped character, or a block of escaped whitespace. #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum StringFragment<'a> { Literal(&'a str), EscapedChar(char), EscapedWS, } /// Combine `parse_literal`, `parse_escaped_whitespace`, and `parse_escaped_char` /// into a `StringFragment`. fn parse_fragment<'a, E>(input: &mut &'a str) -> PResult, E> where E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, { alt(( // The `map` combinator runs a parser, then applies a function to the output // of that parser. parse_literal.map(StringFragment::Literal), parse_escaped_char.map(StringFragment::EscapedChar), parse_escaped_whitespace.value(StringFragment::EscapedWS), )) .parse_next(input) } /// Parse a non-empty block of text that doesn't include \ or " fn parse_literal<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<&'a str, E> { // `take_till1` parses a string of 0 or more characters that aren't one of the // given characters. let not_quote_slash = take_till1(['"', '\\']); // `verify` runs a parser, then runs a verification function on the output of // the parser. The verification function accepts the output only if it // returns true. In this case, we want to ensure that the output of take_till1 // is non-empty. not_quote_slash .verify(|s: &str| !s.is_empty()) .parse_next(input) } // parser combinators are constructed from the bottom up: // first we write parsers for the smallest elements (escaped characters), // then combine them into larger parsers. /// Parse an escaped character: \n, \t, \r, \u{00AC}, etc. fn parse_escaped_char<'a, E>(input: &mut &'a str) -> PResult 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.5.15/src/_topic/arithmetic.rs000064400000000000000000000003661046102023000160050ustar 00000000000000//! # Arithmetic //! //! ## Direct evaluation //! //! ```rust #![doc = include_str!("../../examples/arithmetic/parser.rs")] //! ``` //! //! ## Parse to AST //! //! ```rust #![doc = include_str!("../../examples/arithmetic/parser_ast.rs")] //! ``` winnow-0.5.15/src/_topic/error.rs000064400000000000000000000005221046102023000147770ustar 00000000000000//! # Custom Errors //! //! The most basic error type is [`ParserError`][crate::error::ParserError] //! //! Optional traits include: //! - [`AddContext`][crate::error::AddContext] //! - [`FromExternalError`][crate::error::FromExternalError] //! //! # Example //! //!```rust #![doc = include_str!("../../examples/custom_error.rs")] //!``` winnow-0.5.15/src/_topic/fromstr.rs000064400000000000000000000003251046102023000153430ustar 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.5.15/src/_topic/http.rs000064400000000000000000000001331046102023000146230ustar 00000000000000//! # HTTP //! //! ```rust #![doc = include_str!("../../examples/http/parser.rs")] //! ``` winnow-0.5.15/src/_topic/ini.rs000064400000000000000000000001311046102023000144210ustar 00000000000000//! # INI //! //! ```rust #![doc = include_str!("../../examples/ini/parser.rs")] //! ``` winnow-0.5.15/src/_topic/json.rs000064400000000000000000000001421046102023000146150ustar 00000000000000//! # json //! //! ```rust,ignore #![doc = include_str!("../../examples/json/parser.rs")] //! ``` winnow-0.5.15/src/_topic/language.rs000064400000000000000000000220161046102023000154330ustar 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_till1, //! }; //! //! pub fn peol_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> //! { //! ('%', take_till1(['\n', '\r'])) //! .void() // Output is thrown away. //! .parse_next(i) //! } //! ``` //! //! ### `/* C-style comments */` //! //! Inline comments surrounded with sentinel tags `(*` and `*)`. This version returns an output of `()` //! and does not handle nested comments. //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! error::ParserError, //! token::{tag, take_until0}, //! }; //! //! pub fn pinline_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> { //! ( //! "(*", //! take_until0("*)"), //! "*)" //! ) //! .void() // Output is thrown away. //! .parse_next(i) //! } //! ``` //! //! ## Identifiers //! //! ### `Rust-Style Identifiers` //! //! Parsing identifiers that may start with a letter (or underscore) and may contain underscores, //! letters and numbers may be parsed like this: //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! stream::AsChar, //! token::take_while, //! token::one_of, //! }; //! //! pub fn identifier<'s>(input: &mut &'s str) -> PResult<&'s str> { //! ( //! one_of(|c: char| c.is_alpha() || c == '_'), //! take_while(0.., |c: char| c.is_alphanum() || c == '_') //! ) //! .recognize() //! .parse_next(input) //! } //! ``` //! //! Let's say we apply this to the identifier `hello_world123abc`. The first element of the tuple //! would uses [`one_of`][crate::token::one_of] which would recognize `h`. The tuple ensures that //! `ello_world123abc` will be piped to the next [`take_while`][crate::token::take_while] parser, //! which recognizes every remaining character. However, the tuple returns a tuple of the results //! of its sub-parsers. The [`recognize`][crate::Parser::recognize] parser produces a `&str` of the //! input text that was parsed, which in this case is the entire `&str` `hello_world123abc`. //! //! ## Literal Values //! //! ### Escaped Strings //! //! ```rust #![doc = include_str!("../../examples/string/parser.rs")] //! ``` //! //! See also [`escaped`] and [`escaped_transform`]. //! //! ### Integers //! //! The following recipes all return string slices rather than integer values. How to obtain an //! integer value instead is demonstrated for hexadecimal integers. The others are similar. //! //! The parsers allow the grouping character `_`, which allows one to group the digits by byte, for //! example: `0xA4_3F_11_28`. If you prefer to exclude the `_` character, the lambda to convert from a //! string slice to an integer value is slightly simpler. You can also strip the `_` from the string //! slice that is returned, which is demonstrated in the second hexadecimal number parser. //! //! #### Hexadecimal //! //! The parser outputs the string slice of the digits without the leading `0x`/`0X`. //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::alt, //! combinator::{repeat}, //! combinator::{preceded, terminated}, //! token::one_of, //! token::tag, //! }; //! //! fn hexadecimal<'s>(input: &mut &'s str) -> PResult<&'s str> { // <'a, E: ParserError<&'a str>> //! preceded( //! alt(("0x", "0X")), //! repeat(1.., //! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ())) //! ).map(|()| ()).recognize() //! ).parse_next(input) //! } //! ``` //! //! If you want it to return the integer value instead, use map: //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::alt, //! combinator::{repeat}, //! combinator::{preceded, terminated}, //! token::one_of, //! token::tag, //! }; //! //! fn hexadecimal_value(input: &mut &str) -> PResult { //! preceded( //! alt(("0x", "0X")), //! repeat(1.., //! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ())) //! ).map(|()| ()).recognize() //! ).try_map( //! |out: &str| i64::from_str_radix(&str::replace(&out, "_", ""), 16) //! ).parse_next(input) //! } //! ``` //! //! See also [`hex_uint`] //! //! #### Octal //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::alt, //! combinator::{repeat}, //! combinator::{preceded, terminated}, //! token::one_of, //! token::tag, //! }; //! //! fn octal<'s>(input: &mut &'s str) -> PResult<&'s str> { //! preceded( //! alt(("0o", "0O")), //! repeat(1.., //! terminated(one_of('0'..='7'), repeat(0.., '_').map(|()| ())) //! ).map(|()| ()).recognize() //! ).parse_next(input) //! } //! ``` //! //! #### Binary //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::alt, //! combinator::{repeat}, //! combinator::{preceded, terminated}, //! token::one_of, //! token::tag, //! }; //! //! fn binary<'s>(input: &mut &'s str) -> PResult<&'s str> { //! preceded( //! alt(("0b", "0B")), //! repeat(1.., //! terminated(one_of('0'..='1'), repeat(0.., '_').map(|()| ())) //! ).map(|()| ()).recognize() //! ).parse_next(input) //! } //! ``` //! //! #### Decimal //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::{repeat}, //! combinator::terminated, //! token::one_of, //! }; //! //! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> { //! repeat(1.., //! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ())) //! ).map(|()| ()) //! .recognize() //! .parse_next(input) //! } //! ``` //! //! See also [`dec_uint`] and [`dec_int`] //! //! ### Floating Point Numbers //! //! The following is adapted from [the Python parser by Valentin Lorentz](https://github.com/ProgVal/rust-python-parser/blob/master/src/numbers.rs). //! //! ```rust //! use winnow::prelude::*; //! use winnow::{ //! combinator::alt, //! combinator::{repeat}, //! combinator::opt, //! combinator::{preceded, terminated}, //! token::one_of, //! }; //! //! fn float<'s>(input: &mut &'s str) -> PResult<&'s str> { //! alt(( //! // Case one: .42 //! ( //! '.', //! decimal, //! opt(( //! one_of(['e', 'E']), //! opt(one_of(['+', '-'])), //! decimal //! )) //! ).recognize() //! , // Case two: 42e42 and 42.42e42 //! ( //! decimal, //! opt(preceded( //! '.', //! decimal, //! )), //! one_of(['e', 'E']), //! opt(one_of(['+', '-'])), //! decimal //! ).recognize() //! , // Case three: 42. and 42.42 //! ( //! decimal, //! '.', //! opt(decimal) //! ).recognize() //! )).parse_next(input) //! } //! //! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> { //! repeat(1.., //! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ())) //! ). //! map(|()| ()) //! .recognize() //! .parse_next(input) //! } //! ``` //! //! See also [`float`] #![allow(unused_imports)] use crate::ascii::dec_int; use crate::ascii::dec_uint; use crate::ascii::escaped; use crate::ascii::escaped_transform; use crate::ascii::float; use crate::ascii::hex_uint; winnow-0.5.15/src/_topic/mod.rs000064400000000000000000000015541046102023000144330ustar 00000000000000//! # Special Topics //! //! These are short recipes for accomplishing common tasks. //! //! - [Why `winnow`?][why] //! - Formats: //! - [Elements of Programming Languages][language] //! - [Arithmetic][arithmetic] //! - [s-expression][s_expression] //! - [json] //! - [INI][ini] //! - [HTTP][http] //! - Special Topics: //! - [Implementing `FromStr`][fromstr] //! - [Performance][performance] //! - [Parsing Partial Input][partial] //! - [Custom stream][stream] //! - [Custom errors][error] //! //! See also parsers written with `winnow`: //! //! - [`toml_edit`](https://crates.io/crates/toml_edit) //! - [`hcl-edit`](https://crates.io/crates/hcl-edit) pub mod arithmetic; pub mod error; pub mod fromstr; pub mod http; pub mod ini; pub mod json; pub mod language; pub mod partial; pub mod performance; pub mod s_expression; pub mod stream; pub mod why; winnow-0.5.15/src/_topic/partial.rs000064400000000000000000000035321046102023000153060ustar 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_value`] //! - [`Partial`] can explicitly be changed to being complete once the specified bytes are //! acquired via [`StreamIsPartial::complete`]. //! - A delimiter, like with [ndjson](http://ndjson.org/) //! - You can parse up-to the delimiter or do a `take_until0(delim).and_then(parser)` //! //! If the chunks are not homogeneous, a state machine will be needed to track what the expected //! parser is for the next chunk. //! //! Caveats: //! - `winnow` takes the approach of re-parsing from scratch. Chunks should be relatively small to //! prevent the re-parsing overhead from dominating. //! - Parsers like [`repeat`] do not know when an `eof` is from insufficient data or the end of the //! stream, causing them to always report [`Incomplete`]. //! //! # Example //! //! `main.rs`: //! ```rust,ignore #![doc = include_str!("../../examples/ndjson/main.rs")] //! ``` //! //! `parser.rs`: //! ```rust,ignore #![doc = include_str!("../../examples/ndjson/parser.rs")] //! ``` #![allow(unused_imports)] // Used for intra-doc links use crate::binary::length_value; use crate::combinator::repeat; use crate::error::ErrMode::Incomplete; use crate::error::Needed; use crate::stream::Partial; use crate::stream::StreamIsPartial; winnow-0.5.15/src/_topic/performance.rs000064400000000000000000000035311046102023000161520ustar 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.5.15/src/_topic/s_expression.rs000064400000000000000000000001531046102023000163670ustar 00000000000000//! # s-expression //! //! ```rust #![doc = include_str!("../../examples/s_expression/parser.rs")] //! ``` winnow-0.5.15/src/_topic/stream.rs000064400000000000000000000041521046102023000151440ustar 00000000000000//! # Custom [`Stream`][crate::stream::Stream] //! //! `winnow` is batteries included with support for //! - Basic inputs like `&str`, newtypes with //! - Improved debug output like [`Bytes`][crate::Bytes] //! - [`Stateful`][crate::Stateful] for passing state through your parser, like tracking recursion //! depth //! - [`Located`][crate::Located] for looking up the absolute position of a token //! //! But that won't always cut it for your parser. For example, you might lex `&str` into //! a series of tokens and then want to parse a `TokenStream`. //! //! ## Implementing a custom stream //! //! Let's assume we have an input type we'll call `MyStream`. `MyStream` is a sequence of `MyItem` type. //! The goal is to define parsers with this signature: `&mut MyStream -> PResult`. //! //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::tag; //! # type MyStream<'i> = &'i str; //! # type Output<'i> = &'i str; //! fn parser<'s>(i: &mut MyStream<'s>) -> PResult> { //! "test".parse_next(i) //! } //! ``` //! //! Here are the traits we 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| //! //! Here are the traits we have to implement 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 slices of `MyItem`: //! //! | [`SliceLen`] |Calculate the input length| //! | [`ParseSlice`] |Used to integrate `&str`'s `parse()` method| #[allow(unused_imports)] // Here for intra-dock links use crate::stream::*; winnow-0.5.15/src/_topic/why.rs000064400000000000000000000121111046102023000144520ustar 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][crate::trace] for easier debugging //! - [Streaming parsing][crate::Partial] for network communication or large file //! - [Stateful][crate::Stateful] parsers //! //! For binary formats, `winnow` includes: //! - [A hexadecimal view][crate::Bytes] in [traces][crate::trace] //! - [TLV](https://en.wikipedia.org/wiki/Type-length-value) //! - Some common parsers to help get started, like numbers //! //! For text formats, `winnow` includes: //! - [Tracking of spans][crate::Located] //! - [A textual view when parsing as bytes][crate::BStr] in [traces][crate::trace] //! - Ability to evaluate directly, parse to an AST, or lex and parse the format //! //! This works well for: //! - Prototyping for what will be a hand-written parser //! - When you want to minimize the work to evolve your format //! - When you don't have contributors focused solely on parsing and your grammar is large enough //! to be unwieldy to hand write. //! //! ## `nom` //! //! `winnow` is a fork of the venerable [`nom`](https://crates.io/crates/nom). The difference //! between them is largely in priorities. `nom` prioritizes: //! - Lower churn for existing users while `winnow` is trying to find ways to make things better //! for the parsers yet to be written. //! - Having a small core, relying on external crates like //! [`nom-locate`](https://crates.io/crates/nom_locate) and //! [`nom-supreme`](https://crates.io/crates/nom-supreme), encouraging flexibility among users //! and to not block users on new features being merged while `winnow` aims to include all the //! fundamentals for parsing to ensure the experience is cohesive and high quality. //! //! ## `chumsky` //! //! [`chumsky`](https://crates.io/crates/chumsky) is an up and coming parser-combinator library //! that includes advanced features like error recovery. //! //! Probably the biggest diverging philosophy is `chumsky`s stance: //! //! > "If you need to implement either `Parser` or `Strategy` by hand, that's a problem that needs fixing". //! //! This is under "batteries included" but it also ties into the feeling that `chumksy` acts more like //! a framework. Instead of composing together helpers, you are expected to do everything through //! their system to the point that it is non-trivial to implement their `Parser` trait and are //! encouraged to use the //! [`custom`](https://docs.rs/chumsky/0.9.0/chumsky/primitive/fn.custom.html) helper. This //! requires re-framing everything to fit within their model and makes the code harder to understand //! and debug as you are working with abstract operations that will eventually be applied //! rather than directly with the parsers. //! //! In contrast, `winnow` is an introspectable toolbox that can easily be customized at any level. //! Probably the biggest thing that `winnow` loses out on is optimizations from ["parse modes" via //! GATs](https://github.com/zesterer/chumsky/pull/82) which allows downstream parsers to tell //! upstream parsers when information will be discarded, allowing bypassing expensive operations, //! like allocations. This requires a lot more complex interaction with parsers that isn't as //! trivial to do with bare functions which would lose out on any of that side-band information. //! Instead, we work around this with things like the [`Accumulate`] trait. #![allow(unused_imports)] use crate::stream::Accumulate; winnow-0.5.15/src/_tutorial/chapter_0.rs000064400000000000000000000030411046102023000162370ustar 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 takes an input and gives back an output //! - "combinators", or functions that take parsers and _combine_ them together! //! //! While "combinator" might be an unfamiliar word, you are likely using them in your rust code //! today, like with the [`Iterator`] trait: //! ```rust //! let data = vec![1, 2, 3, 4, 5]; //! let even_count = data.iter() //! .copied() // combinator //! .filter(|d| d % 2 == 0) // combinator //! .count(); // combinator //! ``` //! //! Parser combinators are great because: //! //! - The parsers are small and easy to write //! - The parsers components are easy to reuse (if they're general enough, please add them to winnow!) //! - The parsers components are easy to test separately (unit tests and property-based tests) //! - The parser combination code looks close to the grammar you would have written //! - You can build partial parsers, specific to the data you need at the moment, and ignore the rest #![allow(unused_imports)] use crate::_topic; use std::iter::Iterator; pub use super::chapter_1 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_1.rs000064400000000000000000000062271046102023000162510ustar 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 build simple parsers, and //! then combine them (using "combinators"). //! //! Let's discuss what a "parser" actually does. A parser takes an input and returns //! a result, where: //! - `Ok` indicates the parser successfully found what it was looking for; or //! - `Err` indicates the parser could not find what it was looking for. //! //! Parsers do more than just return a binary "success"/"failure" code. //! On success, the parser will return the processed data. The input will be left pointing to //! data that still needs processing //! //! If the parser failed, then there are multiple errors that could be returned. //! For simplicity, however, in the next chapters we will leave these unexplored. //! //! ```text //! ┌─► Ok(what matched the parser) //! ┌─────────┐ │ //! my input───►│my parser├──►either──┤ //! └─────────┘ └─► Err(...) //! ``` //! //! //! To represent this model of the world, winnow uses the [`PResult`] 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. //! //! 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 remaining input is the same as the input. //! - Since it doesn't parse anything, it also should just return an empty string. //! //! ```rust //! use winnow::PResult; //! use winnow::Parser; //! //! pub fn do_nothing_parser<'s>(input: &mut &'s str) -> PResult<&'s str> { //! Ok("") //! } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let output = do_nothing_parser.parse_next(&mut input).unwrap(); //! // Same as: //! // let output = do_nothing_parser(&mut input).unwrap(); //! //! assert_eq!(input, "0x1a2b Hello"); //! assert_eq!(output, ""); //! } //! ``` #![allow(unused_imports)] use crate::PResult; use crate::Parser; pub use super::chapter_0 as previous; pub use super::chapter_2 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_2.rs000064400000000000000000000166471046102023000162610ustar 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()); //! } //! ``` //! //! [`any`] and [`Parser::verify`] are [`Parser`] building blocks on top of [`Stream`]: //! ```rust //! # use winnow::PResult; //! use winnow::Parser; //! use winnow::token::any; //! //! fn parse_prefix(input: &mut &str) -> PResult { //! any.verify(|c| *c == '0').parse_next(input) //! } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let output = parse_prefix.parse_next(&mut input).unwrap(); //! # //! # assert_eq!(input, "x1a2b Hello"); //! # assert_eq!(output, '0'); //! # //! # assert!(parse_prefix.parse_next(&mut "d").is_err()); //! # } //! ``` //! //! Matching a single token literal is common enough that [`Parser`] is implemented for //! `char`. //! //! ```rust //! # use winnow::PResult; //! use winnow::Parser; //! //! fn parse_prefix(input: &mut &str) -> PResult { //! '0'.parse_next(input) //! } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let output = parse_prefix.parse_next(&mut input).unwrap(); //! # //! # assert_eq!(input, "x1a2b Hello"); //! # assert_eq!(output, '0'); //! # //! # assert!(parse_prefix.parse_next(&mut "d").is_err()); //! # } //! ``` //! //! ## Tags //! //! [`Stream`] also supports processing slices of tokens: //! ```rust //! # use winnow::Parser; //! # use winnow::PResult; //! use winnow::stream::Stream; //! use winnow::error::ParserError; //! use winnow::error::ErrorKind; //! use winnow::error::ErrMode; //! //! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! let expected = "0x"; //! if input.len() < expected.len() { //! return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); //! } //! let actual = input.next_slice(expected.len()); //! if actual != expected { //! return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); //! } //! Ok(actual) //! } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let output = parse_prefix.parse_next(&mut input).unwrap(); //! assert_eq!(input, "1a2b Hello"); //! assert_eq!(output, "0x"); //! //! assert!(parse_prefix.parse_next(&mut "0o123").is_err()); //! } //! ``` //! //! Again, matching a literal is common enough that [`Parser`] is implemented for `&str`: //! ```rust //! # use winnow::PResult; //! use winnow::Parser; //! //! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! "0x".parse_next(input) //! } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let output = parse_prefix.parse_next(&mut input).unwrap(); //! # assert_eq!(input, "1a2b Hello"); //! # assert_eq!(output, "0x"); //! # //! # assert!(parse_prefix.parse_next(&mut "0o123").is_err()); //! # } //! ``` //! //! In `winnow`, we call this type of parser a [`tag`]. See [`token`] for additional individual //! and token-slice parsers. //! //! ## Character Classes //! //! Selecting a single `char` or a [`tag`] is fairly limited. Sometimes, you will want to select one of several //! `chars` of a specific class, like digits. For this, we use the [`one_of`] parer: //! //! ```rust //! # use winnow::Parser; //! # use winnow::PResult; //! use winnow::token::one_of; //! //! fn parse_digits(input: &mut &str) -> PResult { //! 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 with by using one of the built-in character classes, [`hex_digit1`]: //! ```rust //! # use winnow::Parser; //! # use winnow::PResult; //! use winnow::ascii::hex_digit1; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! hex_digit1.parse_next(input) //! } //! //! fn main() { //! let mut input = "1a2b Hello"; //! //! let output = parse_digits.parse_next(&mut input).unwrap(); //! assert_eq!(input, " Hello"); //! assert_eq!(output, "1a2b"); //! //! assert!(parse_digits.parse_next(&mut "Z").is_err()); //! } //! ``` //! //! See [`ascii`] for more text-based parsers. #![allow(unused_imports)] use crate::ascii; use crate::ascii::hex_digit1; use crate::stream::ContainsToken; use crate::stream::Stream; use crate::token; use crate::token::any; use crate::token::one_of; use crate::token::tag; use crate::token::take_while; use crate::Parser; use std::ops::RangeInclusive; pub use super::chapter_1 as previous; pub use super::chapter_3 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_3.rs000064400000000000000000000252471046102023000162560ustar 00000000000000//! # Chapter 3: Sequencing and Alternatives //! //! In the last chapter, we saw how to create simple parsers using prebuilt parsers. //! //! In this chapter, we explore two other widely used features: //! alternatives and composition. //! //! ## Sequencing //! //! Now that we can create more interesting parsers, we can sequence them together, like: //! //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # //! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! "0x".parse_next(input) //! } //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! take_while(1.., ( //! ('0'..='9'), //! ('A'..='F'), //! ('a'..='f'), //! )).parse_next(input) //! } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let prefix = parse_prefix.parse_next(&mut input).unwrap(); //! let digits = parse_digits.parse_next(&mut input).unwrap(); //! //! assert_eq!(prefix, "0x"); //! assert_eq!(digits, "1a2b"); //! assert_eq!(input, " Hello"); //! } //! ``` //! //! To sequence these together, you can just put them in a tuple: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # //! # fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # "0x".parse_next(input) //! # } //! # //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! //... //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let (prefix, digits) = ( //! parse_prefix, //! parse_digits //! ).parse_next(&mut input).unwrap(); //! //! assert_eq!(prefix, "0x"); //! assert_eq!(digits, "1a2b"); //! assert_eq!(input, " Hello"); //! } //! ``` //! //! Frequently, you won't care about the tag and you can instead use one of the provided combinators, //! like [`preceded`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! use winnow::combinator::preceded; //! //! # fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # "0x".parse_next(input) //! # } //! # //! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! //... //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let digits = preceded( //! parse_prefix, //! parse_digits //! ).parse_next(&mut input).unwrap(); //! //! assert_eq!(digits, "1a2b"); //! assert_eq!(input, " Hello"); //! } //! ``` //! //! See [`combinator`] for more sequencing parsers. //! //! ## Alternatives //! //! Sometimes, we might want to choose between two parsers; and we're happy with //! either being used. //! //! [`Stream::checkpoint`] helps us to retry parsing: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! use winnow::stream::Stream; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! let start = input.checkpoint(); //! //! if let Ok(output) = ("0b", parse_bin_digits).parse_next(input) { //! return Ok(output); //! } //! //! input.reset(start); //! if let Ok(output) = ("0o", parse_oct_digits).parse_next(input) { //! return Ok(output); //! } //! //! input.reset(start); //! if let Ok(output) = ("0d", parse_dec_digits).parse_next(input) { //! return Ok(output); //! } //! //! input.reset(start); //! ("0x", parse_hex_digits).parse_next(input) //! } //! //! // ... //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(prefix, "0x"); //! assert_eq!(digits, "1a2b"); //! //! assert!(parse_digits(&mut "ghiWorld").is_err()); //! } //! ``` //! //! > **Warning:** the above example is for illustrative purposes and relying on `Result::Ok` or //! > `Result::Err` can lead to incorrect behavior. This will be clarified in later when covering //! > [error handling][`chapter_6`#errmode] //! //! [`opt`] is a basic building block for correctly handling retrying parsing: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! use winnow::combinator::opt; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! if let Some(output) = opt(("0b", parse_bin_digits)).parse_next(input)? { //! Ok(output) //! } else if let Some(output) = opt(("0o", parse_oct_digits)).parse_next(input)? { //! Ok(output) //! } else if let Some(output) = opt(("0d", parse_dec_digits)).parse_next(input)? { //! Ok(output) //! } else { //! ("0x", parse_hex_digits).parse_next(input) //! } //! } //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap(); //! # //! # assert_eq!(input, " Hello"); //! # assert_eq!(prefix, "0x"); //! # assert_eq!(digits, "1a2b"); //! # //! # assert!(parse_digits(&mut "ghiWorld").is_err()); //! # } //! ``` //! //! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the `else`: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! use winnow::combinator::alt; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! alt(( //! ("0b", parse_bin_digits), //! ("0o", parse_oct_digits), //! ("0d", parse_dec_digits), //! ("0x", parse_hex_digits), //! )).parse_next(input) //! } //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let mut input = "0x1a2b Hello"; //! # //! # let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap(); //! # //! # assert_eq!(input, " Hello"); //! # assert_eq!(prefix, "0x"); //! # assert_eq!(digits, "1a2b"); //! # //! # assert!(parse_digits(&mut "ghiWorld").is_err()); //! # } //! ``` //! //! > **Note:** [`success`] and [`fail`] are parsers that might be useful in the `else` case. //! //! Sometimes a giant if/else-if ladder can be slow and you'd rather have a `match` statement for //! branches of your parser that have unique prefixes. In this case, you can use the //! [`dispatch`][crate::combinator::dispatch] macro: //! //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! use winnow::combinator::dispatch; //! use winnow::token::take; //! use winnow::combinator::fail; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! dispatch!(take(2usize); //! "0b" => parse_bin_digits, //! "0o" => parse_oct_digits, //! "0d" => parse_dec_digits, //! "0x" => parse_hex_digits, //! _ => fail, //! ).parse_next(input) //! } //! //! // ... //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let digits = parse_digits.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(digits, "1a2b"); //! //! assert!(parse_digits(&mut "ghiWorld").is_err()); //! } //! ``` //! //! > **Note:** [`peek`] may be useful when [`dispatch`]ing from hints from each case's parser. //! //! See [`combinator`] for more alternative parsers. #![allow(unused_imports)] use super::chapter_6; use crate::combinator; use crate::combinator::alt; use crate::combinator::dispatch; use crate::combinator::fail; use crate::combinator::opt; use crate::combinator::peek; use crate::combinator::preceded; use crate::combinator::success; use crate::stream::Stream; pub use super::chapter_2 as previous; pub use super::chapter_4 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_4.rs000064400000000000000000000071361046102023000162540ustar 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`. //! Recall that the type parameter of the `PResult` is the input //! type, so even if you're returning something different, if your input //! is a `&str`, the type argument of `PResult` should be also. //! //! One winnow-native way of doing a type conversion is to use the //! [`Parser::parse_to`] combinator //! to convert from a successful parse to a particular type using [`FromStr`]. //! //! The following code converts from a string containing a number to `usize`: //! ```rust //! # use winnow::prelude::*; //! # use winnow::ascii::digit1; //! # //! fn parse_digits(input: &mut &str) -> PResult { //! 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'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let digits = parse_digits.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(digits, 0x1a2b); //! //! assert!(parse_digits(&mut "ghiWorld").is_err()); //! } //! ``` //! //! See also [`Parser`] for more output-modifying parsers. #![allow(unused_imports)] use crate::PResult; use crate::Parser; use std::str::FromStr; pub use super::chapter_3 as previous; pub use super::chapter_5 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_5.rs000064400000000000000000000221121046102023000162440ustar 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'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello"; //! //! let digits = parse_list.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]); //! //! assert!(parse_digits(&mut "ghiWorld").is_err()); //! } //! ``` //! //! We can implement this declaratively with [`repeat`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::dispatch; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! use winnow::combinator::opt; //! use winnow::combinator::repeat; //! use winnow::combinator::terminated; //! //! fn parse_list(input: &mut &str) -> PResult> { //! 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'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! # //! # fn main() { //! # let mut input = "0x1a2b,0x3c4d,0x5e6f Hello"; //! # //! # let digits = parse_list.parse_next(&mut input).unwrap(); //! # //! # assert_eq!(input, " Hello"); //! # assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]); //! # //! # assert!(parse_digits(&mut "ghiWorld").is_err()); //! # } //! ``` //! //! You'll notice that the above allows trailing `,` when we intended to not support that. We can //! easily fix this by using [`separated0`]: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::dispatch; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! use winnow::combinator::separated0; //! //! fn parse_list(input: &mut &str) -> PResult> { //! separated0(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'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello"; //! //! let digits = parse_list.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]); //! //! assert!(parse_digits(&mut "ghiWorld").is_err()); //! } //! ``` //! //! If you look closely at [`repeat`], it isn't collecting directly into a [`Vec`] but //! [`Accumulate`] to gather the results. This let's us make more complex parsers than we did in //! [`chapter_2`] by accumulating the results into a `()` and [`recognize`][Parser::recognize]-ing the captured input: //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::dispatch; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! # use winnow::combinator::separated0; //! # //! fn recognize_list<'s>(input: &mut &'s str) -> PResult<&'s str> { //! parse_list.recognize().parse_next(input) //! } //! //! fn parse_list(input: &mut &str) -> PResult<()> { //! separated0(parse_digits, ",").parse_next(input) //! } //! //! # fn parse_digits(input: &mut &str) -> PResult { //! # dispatch!(take(2usize); //! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)), //! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)), //! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)), //! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)), //! # _ => fail, //! # ).parse_next(input) //! # } //! # //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello"; //! //! let digits = recognize_list.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f"); //! //! assert!(parse_digits(&mut "ghiWorld").is_err()); //! } //! ``` //! See [`combinator`] for more repetition parsers. #![allow(unused_imports)] use super::chapter_2; use super::chapter_3; use crate::combinator; use crate::combinator::repeat; use crate::combinator::separated0; use crate::stream::Accumulate; use crate::Parser; use std::vec::Vec; pub use super::chapter_4 as previous; pub use super::chapter_6 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_6.rs000064400000000000000000000122631046102023000162530ustar 00000000000000//! # Chapter 6: Error Reporting //! //! ## `Error` //! //! Back in [`chapter_1`], we glossed over the `Err` side 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 improve the error quality. //! //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::alt; //! use winnow::error::StrContext; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! alt(( //! ("0b", parse_bin_digits).context(StrContext::Label("binary")), //! ("0o", parse_oct_digits).context(StrContext::Label("octal")), //! ("0d", parse_dec_digits).context(StrContext::Label("decimal")), //! ("0x", parse_hex_digits).context(StrContext::Label("hexadecimal")), //! )).parse_next(input) //! } //! //! // ... //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(prefix, "0x"); //! assert_eq!(digits, "1a2b"); //! } //! ``` //! //! At first glance, this looks correct but what `context` will be reported when parsing `"0b5"`? //! If you remember back to [`chapter_3`], [`alt`] will only report the last error by default which //! means when parsing `"0b5"`, the `context` will be `"hexadecimal"`. //! //! ## `ErrMode` //! //! Let's break down `PResult` one step further: //! ```rust //! # use winnow::error::ErrorKind; //! # use winnow::error::ErrMode; //! pub type OResult = 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::error::StrContext; //! use winnow::combinator::cut_err; //! //! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> { //! alt(( //! ("0b", cut_err(parse_bin_digits)).context(StrContext::Label("binary")), //! ("0o", cut_err(parse_oct_digits)).context(StrContext::Label("octal")), //! ("0d", cut_err(parse_dec_digits)).context(StrContext::Label("decimal")), //! ("0x", cut_err(parse_hex_digits)).context(StrContext::Label("hexadecimal")), //! )).parse_next(input) //! } //! //! // ... //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let mut input = "0x1a2b Hello"; //! //! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap(); //! //! assert_eq!(input, " Hello"); //! assert_eq!(prefix, "0x"); //! assert_eq!(digits, "1a2b"); //! } //! ``` //! Now, when parsing `"0b5"`, the `context` will be `"binary"`. #![allow(unused_imports)] use super::chapter_1; use super::chapter_3; use crate::combinator::alt; use crate::combinator::cut_err; use crate::error::ContextError; use crate::error::ErrMode; use crate::error::ErrMode::*; use crate::error::ErrorKind; use crate::PResult; use crate::Parser; use crate::_topic; pub use super::chapter_5 as previous; pub use super::chapter_7 as next; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/chapter_7.rs000064400000000000000000000072471046102023000162620ustar 00000000000000//! # Chapter 7: Integrating the Parser //! //! So far, we've highlighted how to incrementally parse, but how do we bring this all together //! into our application? //! //! Parsers we've been working with look like: //! ```rust //! # use winnow::error::ContextError; //! # use winnow::error::ErrMode; //! # use winnow::Parser; //! # //! pub fn parser<'s>(input: &mut &'s str) -> PResult<&'s str> { //! // ... //! # Ok("") //! } //! //! type PResult = Result< //! O, //! ErrMode //! >; //! ``` //! 1. We have to decide what to do about the "remainder" of the `input`. //! 2. The [`ErrMode`] is not compatible with the rest of the Rust ecosystem. //! Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static` //! meaning: //! - They implement the [`std::error::Error`] trait //! - They can be sent across threads //! - They are safe to be referenced across threads //! - They do not borrow //! //! winnow provides [`Parser::parse`] to help with this: //! - Ensures we hit [`eof`] //! - Removes the [`ErrMode`] wrapper //! - Wraps the error in [`ParseError`] //! - Provides access to the original [`input`][ParseError::input] with the //! [`offset`][ParseError::offset] of where it failed //! - Provides a default renderer (via [`std::fmt::Display`]) //! ```rust //! # use winnow::prelude::*; //! # use winnow::token::take_while; //! # use winnow::combinator::dispatch; //! # use winnow::token::take; //! # use winnow::combinator::fail; //! use winnow::Parser; //! //! #[derive(Debug, PartialEq, Eq)] //! pub struct Hex(usize); //! //! impl std::str::FromStr for Hex { //! type Err = String; //! //! fn from_str(input: &str) -> Result { //! parse_digits //! .map(Hex) //! .parse(input) //! .map_err(|e| e.to_string()) //! } //! } //! //! // ... //! # 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'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='7'), //! # )).parse_next(input) //! # } //! # //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # )).parse_next(input) //! # } //! # //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> { //! # take_while(1.., ( //! # ('0'..='9'), //! # ('A'..='F'), //! # ('a'..='f'), //! # )).parse_next(input) //! # } //! //! fn main() { //! let input = "0x1a2b"; //! assert_eq!(input.parse::().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 crate::combinator::eof; use crate::error::ErrMode; use crate::error::InputError; use crate::error::ParseError; use crate::PResult; use crate::Parser; pub use super::chapter_6 as previous; pub use crate::_tutorial as table_of_contents; winnow-0.5.15/src/_tutorial/mod.rs000064400000000000000000000003021046102023000151460ustar 00000000000000//! # Tutorial //! //! Table of Contents 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; winnow-0.5.15/src/ascii/mod.rs000064400000000000000000001705331046102023000142520ustar 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::opt; use crate::error::ParserError; use crate::error::{ErrMode, ErrorKind, Needed}; use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial}; use crate::stream::{Compare, CompareResult}; use crate::token::one_of; use crate::token::take_till0; use crate::token::take_while; use crate::trace::trace; use crate::PResult; use crate::Parser; /// Recognizes the string `"\r\n"`. /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; /// # use winnow::ascii::crlf; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// crlf.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n"))); /// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::crlf; /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n"))); /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag)))); /// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(2)))); /// ``` #[inline(always)] pub fn crlf>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, { trace("crlf", "\r\n").parse_next(input) } /// Recognizes a string of any char except `"\r\n"` or `"\n"`. /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::not_line_ending; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// not_line_ending.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("ab\r\nc"), Ok(("\r\nc", "ab"))); /// assert_eq!(parser.parse_peek("ab\nc"), Ok(("\nc", "ab"))); /// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc"))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// assert_eq!(parser.parse_peek("a\rb\nc"), Err(ErrMode::Backtrack(InputError::new("\rb\nc", ErrorKind::Tag )))); /// assert_eq!(parser.parse_peek("a\rbc"), Err(ErrMode::Backtrack(InputError::new("\rbc", ErrorKind::Tag )))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::not_line_ending; /// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Ok((Partial::new("\r\nc"), "ab"))); /// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::new(1)))); /// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rb\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rb\nc"), ErrorKind::Tag )))); /// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rbc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rbc"), ErrorKind::Tag )))); /// ``` #[inline(always)] pub fn not_line_ending>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, ::Token: AsChar + Clone, { trace("not_line_ending", move |input: &mut I| { if ::is_partial_supported() { not_line_ending_::<_, _, true>(input) } else { not_line_ending_::<_, _, false>(input) } }) .parse_next(input) } fn not_line_ending_, const PARTIAL: bool>( input: &mut I, ) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, ::Token: AsChar + Clone, { let res = take_till0(('\r', '\n')).parse_next(input)?; if input.compare("\r") == CompareResult::Ok { let comp = input.compare("\r\n"); match comp { //FIXME: calculate the right index CompareResult::Ok => {} CompareResult::Incomplete if PARTIAL && input.is_partial() => { return Err(ErrMode::Incomplete(Needed::Unknown)); } CompareResult::Incomplete | CompareResult::Error => { let e: ErrorKind = ErrorKind::Tag; return Err(ErrMode::from_error_kind(input, e)); } } } Ok(res) } /// Recognizes an end of line (both `"\n"` and `"\r\n"`). /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::line_ending; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// line_ending.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n"))); /// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::line_ending; /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n"))); /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag)))); /// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn line_ending>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, { trace("line_ending", alt(("\n", "\r\n"))).parse_next(input) } /// Matches a newline character `'\n'`. /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::newline; /// fn parser<'s>(input: &mut &'s str) -> PResult> { /// newline.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("\nc"), Ok(("c", '\n'))); /// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::newline; /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\nc")), Ok((Partial::new("c"), '\n'))); /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify)))); /// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn newline>(input: &mut I) -> PResult where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, { trace("newline", '\n'.map(AsChar::as_char)).parse_next(input) } /// Matches a tab character `'\t'`. /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::tab; /// fn parser<'s>(input: &mut &'s str) -> PResult> { /// tab.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("\tc"), Ok(("c", '\t'))); /// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::tab; /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\tc")), Ok((Partial::new("c"), '\t'))); /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify)))); /// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn tab>(input: &mut I) -> PResult where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, { trace("tab", '\t'.map(AsChar::as_char)).parse_next(input) } /// Recognizes zero or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'` /// /// *Complete version*: Will return the whole input if no terminating token is found (a non /// alphabetic character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphabetic character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::alpha0; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alpha0.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("ab1c"), Ok(("1c", "ab"))); /// assert_eq!(parser.parse_peek("1c"), Ok(("1c", ""))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::alpha0; /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("ab1c")), Ok((Partial::new("1c"), "ab"))); /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("1c")), Ok((Partial::new("1c"), ""))); /// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn alpha0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("alpha0", take_while(0.., AsChar::is_alpha)).parse_next(input) } /// Recognizes one or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'` /// /// *Complete version*: Will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non alphabetic character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphabetic character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::alpha1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alpha1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("aB1c"), Ok(("1c", "aB"))); /// assert_eq!(parser.parse_peek("1c"), Err(ErrMode::Backtrack(InputError::new("1c", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::alpha1; /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("aB1c")), Ok((Partial::new("1c"), "aB"))); /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("1c")), Err(ErrMode::Backtrack(InputError::new(Partial::new("1c"), ErrorKind::Slice)))); /// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn alpha1>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("alpha1", take_while(1.., AsChar::is_alpha)).parse_next(input) } /// Recognizes zero or more ASCII numerical characters: `'0'..='9'` /// /// *Complete version*: Will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non digit character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non digit character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::digit0; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// digit0.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21"))); /// assert_eq!(parser.parse_peek("21"), Ok(("", "21"))); /// assert_eq!(parser.parse_peek("a21c"), Ok(("a21c", ""))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::digit0; /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21"))); /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("a21c")), Ok((Partial::new("a21c"), ""))); /// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn digit0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("digit0", take_while(0.., AsChar::is_dec_digit)).parse_next(input) } /// Recognizes one or more ASCII numerical characters: `'0'..='9'` /// /// *Complete version*: Will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non digit character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non digit character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::digit1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// digit1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21"))); /// assert_eq!(parser.parse_peek("c1"), Err(ErrMode::Backtrack(InputError::new("c1", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::digit1; /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21"))); /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("c1")), Err(ErrMode::Backtrack(InputError::new(Partial::new("c1"), ErrorKind::Slice)))); /// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` /// /// ## Parsing an integer /// /// You can use `digit1` in combination with [`Parser::try_map`][crate::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 I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("digit1", take_while(1.., AsChar::is_dec_digit)).parse_next(input) } /// Recognizes zero or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`, /// `'a'..='f'` /// /// *Complete version*: Will return the whole input if no terminating token is found (a non hexadecimal digit character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non hexadecimal digit character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::hex_digit0; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// hex_digit0.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c"))); /// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", ""))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::hex_digit0; /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c"))); /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); /// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn hex_digit0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("hex_digit0", take_while(0.., AsChar::is_hex_digit)).parse_next(input) } /// Recognizes one or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`, /// `'a'..='f'` /// /// *Complete version*: Will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non hexadecimal digit character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non hexadecimal digit character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::hex_digit1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// hex_digit1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c"))); /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::hex_digit1; /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c"))); /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); /// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn hex_digit1>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("hex_digit1", take_while(1.., AsChar::is_hex_digit)).parse_next(input) } /// Recognizes zero or more octal characters: `'0'..='7'` /// /// *Complete version*: Will return the whole input if no terminating token is found (a non octal /// digit character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non octal digit character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::oct_digit0; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// oct_digit0.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21"))); /// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", ""))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::oct_digit0; /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21"))); /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); /// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn oct_digit0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("oct_digit0", take_while(0.., AsChar::is_oct_digit)).parse_next(input) } /// Recognizes one or more octal characters: `'0'..='7'` /// /// *Complete version*: Will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non octal digit character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non octal digit character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::oct_digit1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// oct_digit1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21"))); /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::oct_digit1; /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21"))); /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); /// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn oct_digit1>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("oct_digit0", take_while(1.., AsChar::is_oct_digit)).parse_next(input) } /// Recognizes zero or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` /// /// *Complete version*: Will return the whole input if no terminating token is found (a non /// alphanumerical character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphanumerical character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::alphanumeric0; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alphanumeric0.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ"))); /// assert_eq!(parser.parse_peek("&Z21c"), Ok(("&Z21c", ""))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::alphanumeric0; /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ"))); /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("&Z21c")), Ok((Partial::new("&Z21c"), ""))); /// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn alphanumeric0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("alphanumeric0", take_while(0.., AsChar::is_alphanum)).parse_next(input) } /// Recognizes one or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` /// /// *Complete version*: Will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non alphanumerical character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non alphanumerical character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::alphanumeric1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alphanumeric1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ"))); /// assert_eq!(parser.parse_peek("&H2"), Err(ErrMode::Backtrack(InputError::new("&H2", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::alphanumeric1; /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ"))); /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("&H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("&H2"), ErrorKind::Slice)))); /// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn alphanumeric1>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar, { trace("alphanumeric1", take_while(1.., AsChar::is_alphanum)).parse_next(input) } /// Recognizes zero or more spaces and tabs. /// /// *Complete version*: Will return the whole input if no terminating token is found (a non space /// character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::space0; /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t"))); /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); /// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn space0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, { trace("space0", take_while(0.., AsChar::is_space)).parse_next(input) } /// Recognizes zero or more spaces and tabs. /// /// *Complete version*: Will return the whole input if no terminating token is found (a non space /// character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::space1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// space1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek(" \t21c"), Ok(("21c", " \t"))); /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::space1; /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t"))); /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); /// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn space1>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, { trace("space1", take_while(1.., AsChar::is_space)).parse_next(input) } /// Recognizes zero or more spaces, tabs, carriage returns and line feeds. /// /// *Complete version*: will return the whole input if no terminating token is found (a non space /// character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::ascii::multispace0; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// multispace0.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r"))); /// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", ""))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::multispace0; /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r"))); /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), ""))); /// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn multispace0>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, { trace("multispace0", take_while(0.., (' ', '\t', '\r', '\n'))).parse_next(input) } /// Recognizes one or more spaces, tabs, carriage returns and line feeds. /// /// *Complete version*: will return an error if there's not enough input data, /// or the whole input if no terminating token is found (a non space character). /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data, /// or if no terminating token is found (a non space character). /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::ascii::multispace1; /// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// multispace1.parse_next(input) /// } /// /// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r"))); /// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::ascii::multispace1; /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r"))); /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice)))); /// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn multispace1>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, { trace("multispace1", take_while(1.., (' ', '\t', '\r', '\n'))).parse_next(input) } /// Decode a decimal unsigned integer (e.g. [`u32`]) /// /// *Complete version*: can parse until the end of input. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. #[doc(alias = "u8")] #[doc(alias = "u16")] #[doc(alias = "u32")] #[doc(alias = "u64")] #[doc(alias = "u128")] pub fn dec_uint>(input: &mut I) -> PResult where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, O: Uint, { trace("dec_uint", move |input: &mut I| { if input.eof_offset() == 0 { if ::is_partial_supported() && input.is_partial() { return Err(ErrMode::Incomplete(Needed::new(1))); } else { return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); } } let mut value = O::default(); for (offset, c) in input.iter_offsets() { match c.as_char().to_digit(10) { Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| { let d = d as u8; v.checked_add(d, sealed::SealedMarker) }) { None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)), Some(v) => value = v, }, None => { if offset == 0 { return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); } else { let _ = input.next_slice(offset); return Ok(value); } } } } if ::is_partial_supported() && input.is_partial() { Err(ErrMode::Incomplete(Needed::new(1))) } else { let _ = input.finish(); Ok(value) } }) .parse_next(input) } /// Metadata for parsing unsigned integers, see [`dec_uint`] pub trait Uint: Default { #[doc(hidden)] fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option; #[doc(hidden)] fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option; } impl Uint for u8 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for u16 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for u32 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for u64 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for u128 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for i8 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for i16 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for i32 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for i64 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } impl Uint for i128 { fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_mul(by as Self) } fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_add(by as Self) } } /// Decode a decimal signed integer (e.g. [`i32`]) /// /// *Complete version*: can parse until the end of input. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. #[doc(alias = "i8")] #[doc(alias = "i16")] #[doc(alias = "i32")] #[doc(alias = "i64")] #[doc(alias = "i128")] pub fn dec_int>(input: &mut I) -> PResult where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, O: Int, { trace("dec_int", move |input: &mut I| { fn sign(token: impl AsChar) -> bool { let token = token.as_char(); token == '+' || token == '-' } let sign = opt(crate::token::one_of(sign).map(AsChar::as_char)) .map(|c| c != Some('-')) .parse_next(input)?; if input.eof_offset() == 0 { if ::is_partial_supported() && input.is_partial() { return Err(ErrMode::Incomplete(Needed::new(1))); } else { return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); } } let mut value = O::default(); for (offset, c) in input.iter_offsets() { match c.as_char().to_digit(10) { Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| { let d = d as u8; if sign { v.checked_add(d, sealed::SealedMarker) } else { v.checked_sub(d, sealed::SealedMarker) } }) { None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)), Some(v) => value = v, }, None => { if offset == 0 { return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); } else { let _ = input.next_slice(offset); return Ok(value); } } } } if ::is_partial_supported() && input.is_partial() { Err(ErrMode::Incomplete(Needed::new(1))) } else { let _ = input.finish(); Ok(value) } }) .parse_next(input) } /// Metadata for parsing signed integers, see [`dec_int`] pub trait Int: Uint { #[doc(hidden)] fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option; } impl Int for i8 { fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_sub(by as Self) } } impl Int for i16 { fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_sub(by as Self) } } impl Int for i32 { fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_sub(by as Self) } } impl Int for i64 { fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_sub(by as Self) } } impl Int for i128 { fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option { self.checked_sub(by as Self) } } /// Decode a variable-width hexadecimal integer (e.g. [`u32`]) /// /// *Complete version*: Will parse until the end of input if it has fewer characters than the type /// supports. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input /// is hit before a hard boundary (non-hex character, more characters than supported). /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// use winnow::ascii::hex_uint; /// /// fn parser<'s>(s: &mut &'s [u8]) -> PResult> { /// 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 I) -> PResult where I: StreamIsPartial, I: Stream, O: HexUint, ::Token: AsChar, ::Slice: AsBStr, { trace("hex_uint", move |input: &mut I| { let invalid_offset = input .offset_for(|c| { let c = c.as_char(); !"0123456789abcdefABCDEF".contains(c) }) .unwrap_or_else(|| input.eof_offset()); let max_nibbles = O::max_nibbles(sealed::SealedMarker); let max_offset = input.offset_at(max_nibbles); let offset = match max_offset { Ok(max_offset) => { if max_offset < invalid_offset { // Overflow return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)); } else { invalid_offset } } Err(_) => { if ::is_partial_supported() && input.is_partial() && invalid_offset == input.eof_offset() { // Only the next byte is guaranteed required return Err(ErrMode::Incomplete(Needed::new(1))); } else { invalid_offset } } }; if offset == 0 { // Must be at least one digit return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); } let parsed = input.next_slice(offset); let mut res = O::default(); for c in parsed.as_bstr() { let nibble = *c as char; let nibble = nibble.to_digit(16).unwrap_or(0) as u8; let nibble = O::from(nibble); res = (res << O::from(4)) + nibble; } Ok(res) }) .parse_next(input) } /// Metadata for parsing hex numbers, see [`hex_uint`] pub trait HexUint: Default + Shl + 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*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::error::Needed::Size; /// use winnow::ascii::float; /// /// fn parser<'s>(s: &mut &'s str) -> PResult> { /// 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 I) -> PResult where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, ::Slice: ParseSlice, ::Token: AsChar + Clone, ::IterOffsets: Clone, I: AsBStr, { trace("float", move |input: &mut I| { let s = recognize_float_or_exceptions(input)?; s.parse_slice() .ok_or_else(|| ErrMode::from_error_kind(input, ErrorKind::Verify)) }) .parse_next(input) } #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug fn recognize_float_or_exceptions>( input: &mut I, ) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, ::Token: AsChar + Clone, ::IterOffsets: Clone, I: AsBStr, { alt(( recognize_float, crate::token::tag_no_case("nan"), crate::token::tag_no_case("infinity"), crate::token::tag_no_case("inf"), )) .parse_next(input) } #[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug fn recognize_float>(input: &mut I) -> PResult<::Slice, E> where I: StreamIsPartial, I: Stream, I: Compare<&'static str>, ::Token: AsChar + Clone, ::IterOffsets: Clone, I: AsBStr, { ( opt(one_of(['+', '-'])), alt(( (digit1, opt(('.', opt(digit1)))).map(|_| ()), ('.', digit1).map(|_| ()), )), opt((one_of(['e', 'E']), opt(one_of(['+', '-'])), cut_err(digit1))), ) .recognize() .parse_next(input) } /// Matches a byte string with escaped characters. /// /// * The first argument matches the normal characters (it must not accept the control character) /// * The second argument is the control character (like `\` in most languages) /// * The third argument matches the escaped characters /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult}; /// # use winnow::ascii::digit1; /// # use winnow::prelude::*; /// use winnow::ascii::escaped; /// use winnow::token::one_of; /// /// fn esc(s: &str) -> IResult<&str, &str> { /// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s) /// } /// /// assert_eq!(esc("123;"), Ok((";", "123"))); /// assert_eq!(esc(r#"12\"34;"#), Ok((";", r#"12\"34"#))); /// ``` /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult}; /// # use winnow::ascii::digit1; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::ascii::escaped; /// use winnow::token::one_of; /// /// fn esc(s: Partial<&str>) -> IResult, &str> { /// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s) /// } /// /// assert_eq!(esc(Partial::new("123;")), Ok((Partial::new(";"), "123"))); /// assert_eq!(esc(Partial::new("12\\\"34;")), Ok((Partial::new(";"), "12\\\"34"))); /// ``` #[inline(always)] pub fn escaped<'a, I: 'a, Error, F, G, O1, O2>( mut normal: F, control_char: char, mut escapable: G, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, F: Parser, G: Parser, Error: ParserError, { trace("escaped", move |input: &mut I| { 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) } }) } fn streaming_escaped_internal( input: &mut I, normal: &mut F, control_char: char, escapable: &mut G, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, 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: 'a, 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, ::Token: crate::stream::AsChar + Clone, 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()) } /// Matches a byte string with escaped characters. /// /// * The first argument matches the normal characters (it must not match the control character) /// * The second argument is the control character (like `\` in most languages) /// * The third argument matches the escaped characters and transforms them /// /// As an example, the chain `abc\tdef` could be `abc def` (it also consumes the control character) /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use std::str::from_utf8; /// use winnow::token::tag; /// use winnow::ascii::escaped_transform; /// use winnow::ascii::alpha1; /// use winnow::combinator::alt; /// /// fn parser<'s>(input: &mut &'s str) -> PResult> { /// escaped_transform( /// alpha1, /// '\\', /// alt(( /// "\\".value("\\"), /// "\"".value("\""), /// "n".value("\n"), /// )) /// ).parse_next(input) /// } /// /// assert_eq!(parser.parse_peek("ab\\\"cd"), Ok(("", String::from("ab\"cd")))); /// assert_eq!(parser.parse_peek("ab\\ncd"), Ok(("", String::from("ab\ncd")))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use std::str::from_utf8; /// # use winnow::Partial; /// use winnow::token::tag; /// use winnow::ascii::escaped_transform; /// use winnow::ascii::alpha1; /// use winnow::combinator::alt; /// /// fn parser<'s>(input: &mut Partial<&'s str>) -> PResult>> { /// 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: F, control_char: char, mut transform: G, ) -> impl Parser where I: StreamIsPartial, I: Stream, ::Token: crate::stream::AsChar + Clone, Output: crate::stream::Accumulate<::Slice>, F: Parser::Slice, Error>, G: Parser::Slice, Error>, Error: ParserError, { trace("escaped_transform", move |input: &mut I| { if ::is_partial_supported() && input.is_partial() { streaming_escaped_transform_internal(input, &mut normal, control_char, &mut transform) } else { complete_escaped_transform_internal(input, &mut normal, control_char, &mut transform) } }) } fn streaming_escaped_transform_internal( input: &mut I, normal: &mut F, control_char: char, transform: &mut G, ) -> PResult where I: StreamIsPartial, I: Stream, ::Token: crate::stream::AsChar + Clone, 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, ::Token: crate::stream::AsChar + Clone, 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.5.15/src/ascii/tests.rs000064400000000000000000001446071046102023000146400ustar 00000000000000use super::*; use crate::prelude::*; mod complete { use super::*; use crate::combinator::alt; use crate::combinator::opt; use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::InputError; use crate::stream::ParseSlice; use crate::token::none_of; use crate::token::one_of; #[cfg(feature = "alloc")] use crate::{lib::std::string::String, lib::std::vec::Vec}; use proptest::prelude::*; macro_rules! assert_parse( ($left: expr, $right: expr) => { let res: $crate::IResult<_, _, InputError<_>> = $left; assert_eq!(res, $right); }; ); #[test] fn character() { let empty: &[u8] = b""; let a: &[u8] = b"abcd"; let b: &[u8] = b"1234"; let c: &[u8] = b"a123"; let d: &[u8] = "azé12".as_bytes(); let e: &[u8] = b" "; let f: &[u8] = b" ;"; //assert_eq!(alpha1::<_, InputError>(a), Err(ErrMode::Incomplete(Needed::Size(1)))); assert_parse!(alpha1.parse_peek(a), Ok((empty, a))); assert_eq!( alpha1.parse_peek(b), Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Slice))) ); assert_eq!( alpha1::<_, InputError<_>>.parse_peek(c), Ok((&c[1..], &b"a"[..])) ); assert_eq!( alpha1::<_, InputError<_>>.parse_peek(d), Ok(("é12".as_bytes(), &b"az"[..])) ); assert_eq!( digit1.parse_peek(a), Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) ); assert_eq!(digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); assert_eq!( digit1.parse_peek(c), Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) ); assert_eq!( digit1.parse_peek(d), Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) ); assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(a), Ok((empty, a))); assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(c), Ok((empty, c))); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(d), Ok(("zé12".as_bytes(), &b"a"[..])) ); assert_eq!( hex_digit1.parse_peek(e), Err(ErrMode::Backtrack(InputError::new(e, ErrorKind::Slice))) ); assert_eq!( oct_digit1.parse_peek(a), Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) ); assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); assert_eq!( oct_digit1.parse_peek(c), Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) ); assert_eq!( oct_digit1.parse_peek(d), Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(a), Ok((empty, a)) ); //assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b))); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(c), Ok((empty, c)) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(d), Ok(("é12".as_bytes(), &b"az"[..])) ); assert_eq!(space1::<_, InputError<_>>.parse_peek(e), Ok((empty, e))); assert_eq!( space1::<_, InputError<_>>.parse_peek(f), Ok((&b";"[..], &b" "[..])) ); } #[cfg(feature = "alloc")] #[test] fn character_s() { let empty = ""; let a = "abcd"; let b = "1234"; let c = "a123"; let d = "azé12"; let e = " "; assert_eq!(alpha1::<_, InputError<_>>.parse_peek(a), Ok((empty, a))); assert_eq!( alpha1.parse_peek(b), Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Slice))) ); assert_eq!(alpha1::<_, InputError<_>>.parse_peek(c), Ok((&c[1..], "a"))); assert_eq!(alpha1::<_, InputError<_>>.parse_peek(d), Ok(("é12", "az"))); assert_eq!( digit1.parse_peek(a), Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) ); assert_eq!(digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); assert_eq!( digit1.parse_peek(c), Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) ); assert_eq!( digit1.parse_peek(d), Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) ); assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(a), Ok((empty, a))); assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(c), Ok((empty, c))); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(d), Ok(("zé12", "a")) ); assert_eq!( hex_digit1.parse_peek(e), Err(ErrMode::Backtrack(InputError::new(e, ErrorKind::Slice))) ); assert_eq!( oct_digit1.parse_peek(a), Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice))) ); assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b))); assert_eq!( oct_digit1.parse_peek(c), Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice))) ); assert_eq!( oct_digit1.parse_peek(d), Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice))) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(a), Ok((empty, a)) ); //assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b))); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(c), Ok((empty, c)) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(d), Ok(("é12", "az")) ); assert_eq!(space1::<_, InputError<_>>.parse_peek(e), Ok((empty, e))); } use crate::stream::Offset; #[test] fn offset() { let a = &b"abcd;"[..]; let b = &b"1234;"[..]; let c = &b"a123;"[..]; let d = &b" \t;"[..]; let e = &b" \t\r\n;"[..]; let f = &b"123abcDEF;"[..]; match alpha1::<_, InputError<_>>.parse_peek(a) { Ok((i, _)) => { assert_eq!(i.offset_from(&a) + i.len(), a.len()); } _ => panic!("wrong return type in offset test for alpha"), } match digit1::<_, InputError<_>>.parse_peek(b) { Ok((i, _)) => { assert_eq!(i.offset_from(&b) + i.len(), b.len()); } _ => panic!("wrong return type in offset test for digit"), } match alphanumeric1::<_, InputError<_>>.parse_peek(c) { Ok((i, _)) => { assert_eq!(i.offset_from(&c) + i.len(), c.len()); } _ => panic!("wrong return type in offset test for alphanumeric"), } match space1::<_, InputError<_>>.parse_peek(d) { Ok((i, _)) => { assert_eq!(i.offset_from(&d) + i.len(), d.len()); } _ => panic!("wrong return type in offset test for space"), } match multispace1::<_, InputError<_>>.parse_peek(e) { Ok((i, _)) => { assert_eq!(i.offset_from(&e) + i.len(), e.len()); } _ => panic!("wrong return type in offset test for multispace"), } match hex_digit1::<_, InputError<_>>.parse_peek(f) { Ok((i, _)) => { assert_eq!(i.offset_from(&f) + i.len(), f.len()); } _ => panic!("wrong return type in offset test for hex_digit"), } match oct_digit1::<_, InputError<_>>.parse_peek(f) { Ok((i, _)) => { assert_eq!(i.offset_from(&f) + i.len(), f.len()); } _ => panic!("wrong return type in offset test for oct_digit"), } } #[test] fn is_not_line_ending_bytes() { let a: &[u8] = b"ab12cd\nefgh"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(a), Ok((&b"\nefgh"[..], &b"ab12cd"[..])) ); let b: &[u8] = b"ab12cd\nefgh\nijkl"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(b), Ok((&b"\nefgh\nijkl"[..], &b"ab12cd"[..])) ); let c: &[u8] = b"ab12cd\r\nefgh\nijkl"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(c), Ok((&b"\r\nefgh\nijkl"[..], &b"ab12cd"[..])) ); let d: &[u8] = b"ab12cd"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(d), Ok((&[][..], d)) ); } #[test] fn is_not_line_ending_str() { let f = "βèƒôřè\rÂßÇáƒƭèř"; assert_eq!( not_line_ending.parse_peek(f), Err(ErrMode::Backtrack(InputError::new( &f[12..], ErrorKind::Tag ))) ); let g2: &str = "ab12cd"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(g2), Ok(("", g2)) ); } #[test] fn hex_digit_test() { let i = &b"0123456789abcdefABCDEF;"[..]; assert_parse!(hex_digit1.parse_peek(i), Ok((&b";"[..], &i[..i.len() - 1]))); let i = &b"g"[..]; assert_parse!( hex_digit1.parse_peek(i), Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice))) ); let i = &b"G"[..]; assert_parse!( hex_digit1.parse_peek(i), Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice))) ); assert!(AsChar::is_hex_digit(b'0')); assert!(AsChar::is_hex_digit(b'9')); assert!(AsChar::is_hex_digit(b'a')); assert!(AsChar::is_hex_digit(b'f')); assert!(AsChar::is_hex_digit(b'A')); assert!(AsChar::is_hex_digit(b'F')); assert!(!AsChar::is_hex_digit(b'g')); assert!(!AsChar::is_hex_digit(b'G')); assert!(!AsChar::is_hex_digit(b'/')); assert!(!AsChar::is_hex_digit(b':')); assert!(!AsChar::is_hex_digit(b'@')); assert!(!AsChar::is_hex_digit(b'\x60')); } #[test] fn oct_digit_test() { let i = &b"01234567;"[..]; assert_parse!(oct_digit1.parse_peek(i), Ok((&b";"[..], &i[..i.len() - 1]))); let i = &b"8"[..]; assert_parse!( oct_digit1.parse_peek(i), Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice))) ); assert!(AsChar::is_oct_digit(b'0')); assert!(AsChar::is_oct_digit(b'7')); assert!(!AsChar::is_oct_digit(b'8')); assert!(!AsChar::is_oct_digit(b'9')); assert!(!AsChar::is_oct_digit(b'a')); assert!(!AsChar::is_oct_digit(b'A')); assert!(!AsChar::is_oct_digit(b'/')); assert!(!AsChar::is_oct_digit(b':')); assert!(!AsChar::is_oct_digit(b'@')); assert!(!AsChar::is_oct_digit(b'\x60')); } #[test] fn full_line_windows() { fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { (not_line_ending, line_ending).parse_peek(i) } let input = b"abc\r\n"; let output = take_full_line(input); assert_eq!(output, Ok((&b""[..], (&b"abc"[..], &b"\r\n"[..])))); } #[test] fn full_line_unix() { fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { (not_line_ending, line_ending).parse_peek(i) } let input = b"abc\n"; let output = take_full_line(input); assert_eq!(output, Ok((&b""[..], (&b"abc"[..], &b"\n"[..])))); } #[test] fn check_windows_lineending() { let input = b"\r\n"; let output = line_ending.parse_peek(&input[..]); assert_parse!(output, Ok((&b""[..], &b"\r\n"[..]))); } #[test] fn check_unix_lineending() { let input = b"\n"; let output = line_ending.parse_peek(&input[..]); assert_parse!(output, Ok((&b""[..], &b"\n"[..]))); } #[test] fn cr_lf() { assert_parse!( crlf.parse_peek(&b"\r\na"[..]), Ok((&b"a"[..], &b"\r\n"[..])) ); assert_parse!( crlf.parse_peek(&b"\r"[..]), Err(ErrMode::Backtrack(error_position!( &&b"\r"[..], ErrorKind::Tag ))) ); assert_parse!( crlf.parse_peek(&b"\ra"[..]), Err(ErrMode::Backtrack(error_position!( &&b"\ra"[..], ErrorKind::Tag ))) ); assert_parse!(crlf.parse_peek("\r\na"), Ok(("a", "\r\n"))); assert_parse!( crlf.parse_peek("\r"), Err(ErrMode::Backtrack(error_position!(&"\r", ErrorKind::Tag))) ); assert_parse!( crlf.parse_peek("\ra"), Err(ErrMode::Backtrack(error_position!(&"\ra", ErrorKind::Tag))) ); } #[test] fn end_of_line() { assert_parse!( line_ending.parse_peek(&b"\na"[..]), Ok((&b"a"[..], &b"\n"[..])) ); assert_parse!( line_ending.parse_peek(&b"\r\na"[..]), Ok((&b"a"[..], &b"\r\n"[..])) ); assert_parse!( line_ending.parse_peek(&b"\r"[..]), Err(ErrMode::Backtrack(error_position!( &&b"\r"[..], ErrorKind::Tag ))) ); assert_parse!( line_ending.parse_peek(&b"\ra"[..]), Err(ErrMode::Backtrack(error_position!( &&b"\ra"[..], ErrorKind::Tag ))) ); assert_parse!(line_ending.parse_peek("\na"), Ok(("a", "\n"))); assert_parse!(line_ending.parse_peek("\r\na"), Ok(("a", "\r\n"))); assert_parse!( line_ending.parse_peek("\r"), Err(ErrMode::Backtrack(error_position!(&"\r", ErrorKind::Tag))) ); assert_parse!( line_ending.parse_peek("\ra"), Err(ErrMode::Backtrack(error_position!(&"\ra", ErrorKind::Tag))) ); } fn digit_to_i16(input: &str) -> IResult<&str, i16> { let i = input; let (i, opt_sign) = opt(alt(('+', '-'))).parse_peek(i)?; let sign = match opt_sign { Some('+') | None => true, Some('-') => false, _ => unreachable!(), }; let (i, s) = digit1::<_, InputError<_>>.parse_peek(i)?; match s.parse_slice() { Some(n) => { if sign { Ok((i, n)) } else { Ok((i, -n)) } } None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)), } } fn digit_to_u32(i: &str) -> IResult<&str, u32> { let (i, s) = digit1.parse_peek(i)?; match s.parse_slice() { Some(n) => Ok((i, n)), None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)), } } proptest! { #[test] #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 fn ints(s in "\\PC*") { let res1 = digit_to_i16(&s); let res2 = dec_int.parse_peek(s.as_str()); assert_eq!(res1, res2); } #[test] #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 fn uints(s in "\\PC*") { let res1 = digit_to_u32(&s); let res2 = dec_uint.parse_peek(s.as_str()); assert_eq!(res1, res2); } } #[test] fn hex_uint_tests() { fn hex_u32(input: &[u8]) -> IResult<&[u8], u32> { hex_uint.parse_peek(input) } assert_parse!( hex_u32(&b";"[..]), Err(ErrMode::Backtrack(error_position!( &&b";"[..], ErrorKind::Slice ))) ); assert_parse!(hex_u32(&b"ff;"[..]), Ok((&b";"[..], 255))); assert_parse!(hex_u32(&b"1be2;"[..]), Ok((&b";"[..], 7138))); assert_parse!(hex_u32(&b"c5a31be2;"[..]), Ok((&b";"[..], 3_315_801_058))); assert_parse!(hex_u32(&b"C5A31be2;"[..]), Ok((&b";"[..], 3_315_801_058))); assert_parse!( hex_u32(&b"00c5a31be2;"[..]), // overflow Err(ErrMode::Backtrack(error_position!( &&b"00c5a31be2;"[..], ErrorKind::Verify ))) ); assert_parse!( hex_u32(&b"c5a31be201;"[..]), // overflow Err(ErrMode::Backtrack(error_position!( &&b"c5a31be201;"[..], ErrorKind::Verify ))) ); assert_parse!(hex_u32(&b"ffffffff;"[..]), Ok((&b";"[..], 4_294_967_295))); assert_parse!( hex_u32(&b"ffffffffffffffff;"[..]), // overflow Err(ErrMode::Backtrack(error_position!( &&b"ffffffffffffffff;"[..], ErrorKind::Verify ))) ); assert_parse!( hex_u32(&b"ffffffffffffffff"[..]), // overflow Err(ErrMode::Backtrack(error_position!( &&b"ffffffffffffffff"[..], ErrorKind::Verify ))) ); assert_parse!(hex_u32(&b"0x1be2;"[..]), Ok((&b"x1be2;"[..], 0))); assert_parse!(hex_u32(&b"12af"[..]), Ok((&b""[..], 0x12af))); } #[test] #[cfg(feature = "std")] fn float_test() { let mut test_cases = vec![ "+3.14", "3.14", "-3.14", "0", "0.0", "1.", ".789", "-.5", "1e7", "-1E-7", ".3e-2", "1.e4", "1.2e4", "12.34", "-1.234E-12", "-1.234e-12", "0.00000000000000000087", ]; for test in test_cases.drain(..) { let expected32 = str::parse::(test).unwrap(); let expected64 = str::parse::(test).unwrap(); println!("now parsing: {} -> {}", test, expected32); let larger = test.to_string(); assert_parse!( float.parse_peek(larger.as_bytes()), Ok((&b""[..], expected32)) ); assert_parse!(float.parse_peek(&larger[..]), Ok(("", expected32))); assert_parse!( float.parse_peek(larger.as_bytes()), Ok((&b""[..], expected64)) ); assert_parse!(float.parse_peek(&larger[..]), Ok(("", expected64))); } let remaining_exponent = "-1.234E-"; assert_parse!( float::<_, f64, _>.parse_peek(remaining_exponent), Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice))) ); let (i, nan) = float::<_, f32, ()>.parse_peek("NaN").unwrap(); assert!(nan.is_nan()); assert_eq!(i, ""); let (i, inf) = float::<_, f32, ()>.parse_peek("inf").unwrap(); assert!(inf.is_infinite()); assert_eq!(i, ""); let (i, inf) = float::<_, f32, ()>.parse_peek("infinity").unwrap(); assert!(inf.is_infinite()); assert_eq!(i, ""); } #[cfg(feature = "std")] fn parse_f64(i: &str) -> IResult<&str, f64, ()> { match super::recognize_float_or_exceptions.parse_peek(i) { Err(e) => Err(e), Ok((i, s)) => { if s.is_empty() { return Err(ErrMode::Backtrack(())); } match s.parse_slice() { Some(n) => Ok((i, n)), None => Err(ErrMode::Backtrack(())), } } } } proptest! { #[test] #[cfg(feature = "std")] #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 fn floats(s in "\\PC*") { println!("testing {}", s); let res1 = parse_f64(&s); let res2 = float::<_, f64, ()>.parse_peek(s.as_str()); assert_eq!(res1, res2); } } // issue #1336 "escaped hangs if normal parser accepts empty" #[test] fn complete_escaped_hang() { // issue #1336 "escaped hangs if normal parser accepts empty" fn escaped_string(input: &str) -> IResult<&str, &str> { use crate::ascii::alpha0; use crate::token::one_of; escaped(alpha0, '\\', one_of(['n'])).parse_peek(input) } escaped_string("7").unwrap(); escaped_string("a7").unwrap(); } #[test] fn complete_escaped_hang_1118() { // issue ##1118 escaped does not work with empty string fn unquote(input: &str) -> IResult<&str, &str> { use crate::combinator::delimited; use crate::combinator::opt; use crate::token::one_of; delimited( '"', escaped( opt(none_of(['\\', '"'])), '\\', one_of(['\\', '"', 'r', 'n', 't']), ), '"', ) .parse_peek(input) } assert_eq!(unquote(r#""""#), Ok(("", ""))); } #[cfg(feature = "alloc")] #[allow(unused_variables)] #[test] fn complete_escaping() { use crate::ascii::{alpha1 as alpha, digit1 as digit}; use crate::token::one_of; fn esc(i: &[u8]) -> IResult<&[u8], &[u8]> { escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) } assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], &b"abcd"[..]))); assert_eq!(esc(&b"ab\\\"cd;"[..]), Ok((&b";"[..], &b"ab\\\"cd"[..]))); assert_eq!(esc(&b"\\\"abcd;"[..]), Ok((&b";"[..], &b"\\\"abcd"[..]))); assert_eq!(esc(&b"\\n;"[..]), Ok((&b";"[..], &b"\\n"[..]))); assert_eq!(esc(&b"ab\\\"12"[..]), Ok((&b"12"[..], &b"ab\\\""[..]))); assert_eq!( esc(&b"AB\\"[..]), Err(ErrMode::Backtrack(error_position!( &&b""[..], ErrorKind::Token ))) ); assert_eq!( esc(&b"AB\\A"[..]), Err(ErrMode::Backtrack(error_node_position!( &&b"AB\\A"[..], ErrorKind::Token, error_position!(&&b"A"[..], ErrorKind::Verify) ))) ); fn esc2(i: &[u8]) -> IResult<&[u8], &[u8]> { escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) } assert_eq!(esc2(&b"12\\nnn34"[..]), Ok((&b"nn34"[..], &b"12\\n"[..]))); } #[cfg(feature = "alloc")] #[test] fn complete_escaping_str() { use crate::ascii::{alpha1 as alpha, digit1 as digit}; use crate::token::one_of; fn esc(i: &str) -> IResult<&str, &str> { escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) } assert_eq!(esc("abcd;"), Ok((";", "abcd"))); assert_eq!(esc("ab\\\"cd;"), Ok((";", "ab\\\"cd"))); assert_eq!(esc("\\\"abcd;"), Ok((";", "\\\"abcd"))); assert_eq!(esc("\\n;"), Ok((";", "\\n"))); assert_eq!(esc("ab\\\"12"), Ok(("12", "ab\\\""))); assert_eq!( esc("AB\\"), Err(ErrMode::Backtrack(error_position!(&"", ErrorKind::Token))) ); assert_eq!( esc("AB\\A"), Err(ErrMode::Backtrack(error_node_position!( &"AB\\A", ErrorKind::Token, error_position!(&"A", ErrorKind::Verify) ))) ); fn esc2(i: &str) -> IResult<&str, &str> { escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i) } assert_eq!(esc2("12\\nnn34"), Ok(("nn34", "12\\n"))); fn esc3(i: &str) -> IResult<&str, &str> { escaped(alpha, '\u{241b}', one_of(['\"', 'n'])).parse_peek(i) } assert_eq!(esc3("ab␛ncd;"), Ok((";", "ab␛ncd"))); } #[test] fn test_escaped_error() { fn esc(s: &str) -> IResult<&str, &str> { use crate::ascii::digit1; escaped(digit1, '\\', one_of(['\"', 'n', '\\'])).parse_peek(s) } assert_eq!(esc("abcd"), Ok(("abcd", ""))); } #[cfg(feature = "alloc")] #[test] fn complete_escape_transform() { use crate::ascii::alpha1 as alpha; #[cfg(feature = "alloc")] fn to_s(i: Vec) -> String { String::from_utf8_lossy(&i).into_owned() } fn esc(i: &[u8]) -> IResult<&[u8], String> { escaped_transform( alpha, '\\', alt(( "\\".value(&b"\\"[..]), "\"".value(&b"\""[..]), "n".value(&b"\n"[..]), )), ) .map(to_s) .parse_peek(i) } assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], String::from("abcd")))); assert_eq!( esc(&b"ab\\\"cd;"[..]), Ok((&b";"[..], String::from("ab\"cd"))) ); assert_eq!( esc(&b"\\\"abcd;"[..]), Ok((&b";"[..], String::from("\"abcd"))) ); assert_eq!(esc(&b"\\n;"[..]), Ok((&b";"[..], String::from("\n")))); assert_eq!( esc(&b"ab\\\"12"[..]), Ok((&b"12"[..], String::from("ab\""))) ); assert_eq!( esc(&b"AB\\"[..]), Err(ErrMode::Backtrack(error_position!( &&b""[..], ErrorKind::Tag ))) ); assert_eq!( esc(&b"AB\\A"[..]), Err(ErrMode::Backtrack(error_node_position!( &&b"AB\\A"[..], ErrorKind::Eof, error_position!(&&b"A"[..], ErrorKind::Tag) ))) ); fn esc2(i: &[u8]) -> IResult<&[u8], String> { escaped_transform( alpha, '&', alt(( "egrave;".value("è".as_bytes()), "agrave;".value("à".as_bytes()), )), ) .map(to_s) .parse_peek(i) } assert_eq!( esc2(&b"abèDEF;"[..]), Ok((&b";"[..], String::from("abèDEF"))) ); assert_eq!( esc2(&b"abèDàEF;"[..]), Ok((&b";"[..], String::from("abèDàEF"))) ); } #[cfg(feature = "std")] #[test] fn complete_escape_transform_str() { use crate::ascii::alpha1 as alpha; fn esc(i: &str) -> IResult<&str, String> { escaped_transform( alpha, '\\', alt(("\\".value("\\"), "\"".value("\""), "n".value("\n"))), ) .parse_peek(i) } assert_eq!(esc("abcd;"), Ok((";", String::from("abcd")))); assert_eq!(esc("ab\\\"cd;"), Ok((";", String::from("ab\"cd")))); assert_eq!(esc("\\\"abcd;"), Ok((";", String::from("\"abcd")))); assert_eq!(esc("\\n;"), Ok((";", String::from("\n")))); assert_eq!(esc("ab\\\"12"), Ok(("12", String::from("ab\"")))); assert_eq!( esc("AB\\"), Err(ErrMode::Backtrack(error_position!(&"", ErrorKind::Tag))) ); assert_eq!( esc("AB\\A"), Err(ErrMode::Backtrack(error_node_position!( &"AB\\A", ErrorKind::Eof, error_position!(&"A", ErrorKind::Tag) ))) ); fn esc2(i: &str) -> IResult<&str, String> { escaped_transform( alpha, '&', alt(("egrave;".value("è"), "agrave;".value("à"))), ) .parse_peek(i) } assert_eq!(esc2("abèDEF;"), Ok((";", String::from("abèDEF")))); assert_eq!( esc2("abèDàEF;"), Ok((";", String::from("abèDàEF"))) ); fn esc3(i: &str) -> IResult<&str, String> { escaped_transform(alpha, '␛', alt(("0".value("\0"), "n".value("\n")))).parse_peek(i) } assert_eq!(esc3("a␛0bc␛n"), Ok(("", String::from("a\0bc\n")))); } #[test] #[cfg(feature = "alloc")] fn test_escaped_transform_error() { fn esc_trans(s: &str) -> IResult<&str, String> { use crate::ascii::digit1; escaped_transform(digit1, '\\', "n").parse_peek(s) } assert_eq!(esc_trans("abcd"), Ok(("abcd", String::new()))); } } mod partial { use super::*; use crate::combinator::opt; use crate::error::ErrorKind; use crate::error::InputError; use crate::error::{ErrMode, Needed}; use crate::stream::ParseSlice; use crate::IResult; use crate::Partial; use proptest::prelude::*; macro_rules! assert_parse( ($left: expr, $right: expr) => { let res: $crate::IResult<_, _, InputError<_>> = $left; assert_eq!(res, $right); }; ); #[test] fn character() { let a: &[u8] = b"abcd"; let b: &[u8] = b"1234"; let c: &[u8] = b"a123"; let d: &[u8] = "azé12".as_bytes(); let e: &[u8] = b" "; let f: &[u8] = b" ;"; //assert_eq!(alpha1::<_, Error<_>>(a), Err(ErrMode::Incomplete(Needed::new(1)))); assert_parse!( alpha1.parse_peek(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( alpha1.parse_peek(Partial::new(b)), Err(ErrMode::Backtrack(InputError::new( Partial::new(b), ErrorKind::Slice ))) ); assert_eq!( alpha1::<_, InputError<_>>.parse_peek(Partial::new(c)), Ok((Partial::new(&c[1..]), &b"a"[..])) ); assert_eq!( alpha1::<_, InputError<_>>.parse_peek(Partial::new(d)), Ok((Partial::new("é12".as_bytes()), &b"az"[..])) ); assert_eq!( digit1.parse_peek(Partial::new(a)), Err(ErrMode::Backtrack(InputError::new( Partial::new(a), ErrorKind::Slice ))) ); assert_eq!( digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( digit1.parse_peek(Partial::new(c)), Err(ErrMode::Backtrack(InputError::new( Partial::new(c), ErrorKind::Slice ))) ); assert_eq!( digit1.parse_peek(Partial::new(d)), Err(ErrMode::Backtrack(InputError::new( Partial::new(d), ErrorKind::Slice ))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(c)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(d)), Ok((Partial::new("zé12".as_bytes()), &b"a"[..])) ); assert_eq!( hex_digit1.parse_peek(Partial::new(e)), Err(ErrMode::Backtrack(InputError::new( Partial::new(e), ErrorKind::Slice ))) ); assert_eq!( oct_digit1.parse_peek(Partial::new(a)), Err(ErrMode::Backtrack(InputError::new( Partial::new(a), ErrorKind::Slice ))) ); assert_eq!( oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( oct_digit1.parse_peek(Partial::new(c)), Err(ErrMode::Backtrack(InputError::new( Partial::new(c), ErrorKind::Slice ))) ); assert_eq!( oct_digit1.parse_peek(Partial::new(d)), Err(ErrMode::Backtrack(InputError::new( Partial::new(d), ErrorKind::Slice ))) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); //assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b))); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(d)), Ok((Partial::new("é12".as_bytes()), &b"az"[..])) ); assert_eq!( space1::<_, InputError<_>>.parse_peek(Partial::new(e)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( space1::<_, InputError<_>>.parse_peek(Partial::new(f)), Ok((Partial::new(&b";"[..]), &b" "[..])) ); } #[cfg(feature = "alloc")] #[test] fn character_s() { let a = "abcd"; let b = "1234"; let c = "a123"; let d = "azé12"; let e = " "; assert_eq!( alpha1::<_, InputError<_>>.parse_peek(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( alpha1.parse_peek(Partial::new(b)), Err(ErrMode::Backtrack(InputError::new( Partial::new(b), ErrorKind::Slice ))) ); assert_eq!( alpha1::<_, InputError<_>>.parse_peek(Partial::new(c)), Ok((Partial::new(&c[1..]), "a")) ); assert_eq!( alpha1::<_, InputError<_>>.parse_peek(Partial::new(d)), Ok((Partial::new("é12"), "az")) ); assert_eq!( digit1.parse_peek(Partial::new(a)), Err(ErrMode::Backtrack(InputError::new( Partial::new(a), ErrorKind::Slice ))) ); assert_eq!( digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( digit1.parse_peek(Partial::new(c)), Err(ErrMode::Backtrack(InputError::new( Partial::new(c), ErrorKind::Slice ))) ); assert_eq!( digit1.parse_peek(Partial::new(d)), Err(ErrMode::Backtrack(InputError::new( Partial::new(d), ErrorKind::Slice ))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(c)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(d)), Ok((Partial::new("zé12"), "a")) ); assert_eq!( hex_digit1.parse_peek(Partial::new(e)), Err(ErrMode::Backtrack(InputError::new( Partial::new(e), ErrorKind::Slice ))) ); assert_eq!( oct_digit1.parse_peek(Partial::new(a)), Err(ErrMode::Backtrack(InputError::new( Partial::new(a), ErrorKind::Slice ))) ); assert_eq!( oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( oct_digit1.parse_peek(Partial::new(c)), Err(ErrMode::Backtrack(InputError::new( Partial::new(c), ErrorKind::Slice ))) ); assert_eq!( oct_digit1.parse_peek(Partial::new(d)), Err(ErrMode::Backtrack(InputError::new( Partial::new(d), ErrorKind::Slice ))) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); //assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b))); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(d)), Ok((Partial::new("é12"), "az")) ); assert_eq!( space1::<_, InputError<_>>.parse_peek(Partial::new(e)), Err(ErrMode::Incomplete(Needed::new(1))) ); } use crate::stream::Offset; #[test] fn offset() { let a = &b"abcd;"[..]; let b = &b"1234;"[..]; let c = &b"a123;"[..]; let d = &b" \t;"[..]; let e = &b" \t\r\n;"[..]; let f = &b"123abcDEF;"[..]; match alpha1::<_, InputError<_>>.parse_peek(Partial::new(a)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&a) + i.len(), a.len()); } _ => panic!("wrong return type in offset test for alpha"), } match digit1::<_, InputError<_>>.parse_peek(Partial::new(b)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&b) + i.len(), b.len()); } _ => panic!("wrong return type in offset test for digit"), } match alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&c) + i.len(), c.len()); } _ => panic!("wrong return type in offset test for alphanumeric"), } match space1::<_, InputError<_>>.parse_peek(Partial::new(d)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&d) + i.len(), d.len()); } _ => panic!("wrong return type in offset test for space"), } match multispace1::<_, InputError<_>>.parse_peek(Partial::new(e)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&e) + i.len(), e.len()); } _ => panic!("wrong return type in offset test for multispace"), } match hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(f)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&f) + i.len(), f.len()); } _ => panic!("wrong return type in offset test for hex_digit"), } match oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(f)) { Ok((i, _)) => { let i = i.into_inner(); assert_eq!(i.offset_from(&f) + i.len(), f.len()); } _ => panic!("wrong return type in offset test for oct_digit"), } } #[test] fn is_not_line_ending_bytes() { let a: &[u8] = b"ab12cd\nefgh"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(a)), Ok((Partial::new(&b"\nefgh"[..]), &b"ab12cd"[..])) ); let b: &[u8] = b"ab12cd\nefgh\nijkl"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(b)), Ok((Partial::new(&b"\nefgh\nijkl"[..]), &b"ab12cd"[..])) ); let c: &[u8] = b"ab12cd\r\nefgh\nijkl"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(c)), Ok((Partial::new(&b"\r\nefgh\nijkl"[..]), &b"ab12cd"[..])) ); let d: &[u8] = b"ab12cd"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1))) ); } #[test] fn is_not_line_ending_str() { let f = "βèƒôřè\rÂßÇáƒƭèř"; assert_eq!( not_line_ending.parse_peek(Partial::new(f)), Err(ErrMode::Backtrack(InputError::new( Partial::new(&f[12..]), ErrorKind::Tag ))) ); let g2: &str = "ab12cd"; assert_eq!( not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(g2)), Err(ErrMode::Incomplete(Needed::new(1))) ); } #[test] fn hex_digit_test() { let i = &b"0123456789abcdefABCDEF;"[..]; assert_parse!( hex_digit1.parse_peek(Partial::new(i)), Ok((Partial::new(&b";"[..]), &i[..i.len() - 1])) ); let i = &b"g"[..]; assert_parse!( hex_digit1.parse_peek(Partial::new(i)), Err(ErrMode::Backtrack(error_position!( &Partial::new(i), ErrorKind::Slice ))) ); let i = &b"G"[..]; assert_parse!( hex_digit1.parse_peek(Partial::new(i)), Err(ErrMode::Backtrack(error_position!( &Partial::new(i), ErrorKind::Slice ))) ); assert!(AsChar::is_hex_digit(b'0')); assert!(AsChar::is_hex_digit(b'9')); assert!(AsChar::is_hex_digit(b'a')); assert!(AsChar::is_hex_digit(b'f')); assert!(AsChar::is_hex_digit(b'A')); assert!(AsChar::is_hex_digit(b'F')); assert!(!AsChar::is_hex_digit(b'g')); assert!(!AsChar::is_hex_digit(b'G')); assert!(!AsChar::is_hex_digit(b'/')); assert!(!AsChar::is_hex_digit(b':')); assert!(!AsChar::is_hex_digit(b'@')); assert!(!AsChar::is_hex_digit(b'\x60')); } #[test] fn oct_digit_test() { let i = &b"01234567;"[..]; assert_parse!( oct_digit1.parse_peek(Partial::new(i)), Ok((Partial::new(&b";"[..]), &i[..i.len() - 1])) ); let i = &b"8"[..]; assert_parse!( oct_digit1.parse_peek(Partial::new(i)), Err(ErrMode::Backtrack(error_position!( &Partial::new(i), ErrorKind::Slice ))) ); assert!(AsChar::is_oct_digit(b'0')); assert!(AsChar::is_oct_digit(b'7')); assert!(!AsChar::is_oct_digit(b'8')); assert!(!AsChar::is_oct_digit(b'9')); assert!(!AsChar::is_oct_digit(b'a')); assert!(!AsChar::is_oct_digit(b'A')); assert!(!AsChar::is_oct_digit(b'/')); assert!(!AsChar::is_oct_digit(b':')); assert!(!AsChar::is_oct_digit(b'@')); assert!(!AsChar::is_oct_digit(b'\x60')); } #[test] fn full_line_windows() { #[allow(clippy::type_complexity)] fn take_full_line(i: Partial<&[u8]>) -> IResult, (&[u8], &[u8])> { (not_line_ending, line_ending).parse_peek(i) } let input = b"abc\r\n"; let output = take_full_line(Partial::new(input)); assert_eq!( output, Ok((Partial::new(&b""[..]), (&b"abc"[..], &b"\r\n"[..]))) ); } #[test] fn full_line_unix() { #[allow(clippy::type_complexity)] fn take_full_line(i: Partial<&[u8]>) -> IResult, (&[u8], &[u8])> { (not_line_ending, line_ending).parse_peek(i) } let input = b"abc\n"; let output = take_full_line(Partial::new(input)); assert_eq!( output, Ok((Partial::new(&b""[..]), (&b"abc"[..], &b"\n"[..]))) ); } #[test] fn check_windows_lineending() { let input = b"\r\n"; let output = line_ending.parse_peek(Partial::new(&input[..])); assert_parse!(output, Ok((Partial::new(&b""[..]), &b"\r\n"[..]))); } #[test] fn check_unix_lineending() { let input = b"\n"; let output = line_ending.parse_peek(Partial::new(&input[..])); assert_parse!(output, Ok((Partial::new(&b""[..]), &b"\n"[..]))); } #[test] fn cr_lf() { assert_parse!( crlf.parse_peek(Partial::new(&b"\r\na"[..])), Ok((Partial::new(&b"a"[..]), &b"\r\n"[..])) ); assert_parse!( crlf.parse_peek(Partial::new(&b"\r"[..])), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_parse!( crlf.parse_peek(Partial::new(&b"\ra"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"\ra"[..]), ErrorKind::Tag ))) ); assert_parse!( crlf.parse_peek(Partial::new("\r\na")), Ok((Partial::new("a"), "\r\n")) ); assert_parse!( crlf.parse_peek(Partial::new("\r")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_parse!( crlf.parse_peek(Partial::new("\ra")), Err(ErrMode::Backtrack(error_position!( &Partial::new("\ra"), ErrorKind::Tag ))) ); } #[test] fn end_of_line() { assert_parse!( line_ending.parse_peek(Partial::new(&b"\na"[..])), Ok((Partial::new(&b"a"[..]), &b"\n"[..])) ); assert_parse!( line_ending.parse_peek(Partial::new(&b"\r\na"[..])), Ok((Partial::new(&b"a"[..]), &b"\r\n"[..])) ); assert_parse!( line_ending.parse_peek(Partial::new(&b"\r"[..])), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_parse!( line_ending.parse_peek(Partial::new(&b"\ra"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"\ra"[..]), ErrorKind::Tag ))) ); assert_parse!( line_ending.parse_peek(Partial::new("\na")), Ok((Partial::new("a"), "\n")) ); assert_parse!( line_ending.parse_peek(Partial::new("\r\na")), Ok((Partial::new("a"), "\r\n")) ); assert_parse!( line_ending.parse_peek(Partial::new("\r")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_parse!( line_ending.parse_peek(Partial::new("\ra")), Err(ErrMode::Backtrack(error_position!( &Partial::new("\ra"), ErrorKind::Tag ))) ); } fn digit_to_i16(input: Partial<&str>) -> IResult, i16> { let i = input; let (i, opt_sign) = opt(one_of(['+', '-'])).parse_peek(i)?; let sign = match opt_sign { Some('+') | None => true, Some('-') => false, _ => unreachable!(), }; let (i, s) = digit1::<_, InputError<_>>.parse_peek(i)?; match s.parse_slice() { Some(n) => { if sign { Ok((i, n)) } else { Ok((i, -n)) } } None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)), } } fn digit_to_u32(i: Partial<&str>) -> IResult, u32> { let (i, s) = digit1.parse_peek(i)?; match s.parse_slice() { Some(n) => Ok((i, n)), None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)), } } proptest! { #[test] #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 fn ints(s in "\\PC*") { let res1 = digit_to_i16(Partial::new(&s)); let res2 = dec_int.parse_peek(Partial::new(s.as_str())); assert_eq!(res1, res2); } #[test] #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 fn uints(s in "\\PC*") { let res1 = digit_to_u32(Partial::new(&s)); let res2 = dec_uint.parse_peek(Partial::new(s.as_str())); assert_eq!(res1, res2); } } #[test] fn hex_uint_tests() { fn hex_u32(input: Partial<&[u8]>) -> IResult, 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.5.15/src/binary/bits/mod.rs000064400000000000000000000270571046102023000154110ustar 00000000000000//! Bit level parsers //! #[cfg(test)] mod tests; use crate::error::{ErrMode, ErrorConvert, ErrorKind, Needed, ParserError}; use crate::lib::std::ops::{AddAssign, Div, Shl, Shr}; use crate::stream::{AsBytes, Stream, StreamIsPartial, ToUsize}; use crate::trace::trace; use crate::{unpeek, IResult, PResult, Parser}; /// Number of bits in a byte const BYTE: usize = u8::BITS as usize; /// Converts a byte-level input to a bit-level input /// /// See [`bytes`] to convert it back. /// /// # Example /// ``` /// use winnow::prelude::*; /// use winnow::Bytes; /// use winnow::binary::bits::{bits, take}; /// use winnow::error::InputError; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parse(input: Stream<'_>) -> IResult, (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: P) -> impl Parser where E1: ParserError<(I, usize)> + ErrorConvert, E2: ParserError, I: Stream + Clone, P: Parser<(I, usize), O, E1>, { trace( "bits", unpeek(move |input: I| { match parser.parse_peek((input, 0)) { Ok(((rest, offset), result)) => { // If the next byte has been partially read, it will be sliced away as well. // The parser functions might already slice away all fully read bytes. // That's why `offset / BYTE` isn't necessarily needed at all times. let remaining_bytes_index = offset / BYTE + if offset % BYTE == 0 { 0 } else { 1 }; let (input, _) = rest.peek_slice(remaining_bytes_index); Ok((input, result)) } Err(ErrMode::Incomplete(n)) => { Err(ErrMode::Incomplete(n.map(|u| u.get() / BYTE + 1))) } Err(e) => Err(e.convert()), } }), ) } /// Convert a [`bits`] stream back into a byte stream /// /// **Warning:** A partial byte remaining in the input will be ignored and the given parser will /// start parsing at the next full byte. /// /// ``` /// use winnow::prelude::*; /// use winnow::Bytes; /// use winnow::binary::bits::{bits, bytes, take}; /// use winnow::combinator::rest; /// use winnow::error::InputError; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parse(input: Stream<'_>) -> IResult, (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: P) -> impl Parser<(I, usize), O, E2> where E1: ParserError + ErrorConvert, E2: ParserError<(I, usize)>, I: Stream + Clone, P: Parser, { trace( "bytes", unpeek(move |(input, offset): (I, usize)| { let (inner, _) = if offset % BYTE != 0 { input.peek_slice(1 + offset / BYTE) } else { input.peek_slice(offset / BYTE) }; let i = (input, offset); match parser.parse_peek(inner) { Ok((rest, res)) => Ok(((rest, 0), res)), Err(ErrMode::Incomplete(Needed::Unknown)) => { Err(ErrMode::Incomplete(Needed::Unknown)) } Err(ErrMode::Incomplete(Needed::Size(sz))) => { Err(match sz.get().checked_mul(BYTE) { Some(v) => ErrMode::Incomplete(Needed::new(v)), None => ErrMode::Cut(E2::assert( &i, "overflow in turning needed bytes into needed bits", )), }) } Err(e) => Err(e.convert()), } }), ) } /// Parse taking `count` bits /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::Bytes; /// # use winnow::error::{InputError, ErrorKind}; /// use winnow::binary::bits::take; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parser(input: (Stream<'_>, usize), count: usize)-> IResult<(Stream<'_>, usize), u8> { /// take(count).parse_peek(input) /// } /// /// // Consumes 0 bits, returns 0 /// assert_eq!(parser((stream(&[0b00010010]), 0), 0), Ok(((stream(&[0b00010010]), 0), 0))); /// /// // Consumes 4 bits, returns their values and increase offset to 4 /// assert_eq!(parser((stream(&[0b00010010]), 0), 4), Ok(((stream(&[0b00010010]), 4), 0b00000001))); /// /// // Consumes 4 bits, offset is 4, returns their values and increase offset to 0 of next byte /// assert_eq!(parser((stream(&[0b00010010]), 4), 4), Ok(((stream(&[]), 0), 0b00000010))); /// /// // Tries to consume 12 bits but only 8 are available /// assert_eq!(parser((stream(&[0b00010010]), 0), 12), Err(winnow::error::ErrMode::Backtrack(InputError::new((stream(&[0b00010010]), 0), ErrorKind::Eof)))); /// ``` #[inline(always)] pub fn take>(count: C) -> impl Parser<(I, usize), O, E> where I: Stream + AsBytes + StreamIsPartial + Clone, C: ToUsize, O: From + AddAssign + Shl + Shr, { let count = count.to_usize(); trace( "take", unpeek(move |input: (I, usize)| { if ::is_partial_supported() { take_::<_, _, _, true>(input, count) } else { take_::<_, _, _, false>(input, count) } }), ) } fn take_, const PARTIAL: bool>( (input, bit_offset): (I, usize), count: usize, ) -> IResult<(I, usize), O, E> where I: StreamIsPartial, I: Stream + AsBytes + Clone, O: From + AddAssign + Shl + Shr, { if count == 0 { Ok(((input, bit_offset), 0u8.into())) } else { if input.eof_offset() * BYTE < count + bit_offset { if PARTIAL && input.is_partial() { Err(ErrMode::Incomplete(Needed::new(count))) } else { Err(ErrMode::from_error_kind( &(input, bit_offset), ErrorKind::Eof, )) } } else { let cnt = (count + bit_offset).div(BYTE); let mut acc: O = 0_u8.into(); let mut offset: usize = bit_offset; let mut remaining: usize = count; let mut end_offset: usize = 0; for byte in input.as_bytes().iter().copied().take(cnt + 1) { if remaining == 0 { break; } let val: O = if offset == 0 { byte.into() } else { (byte << offset >> offset).into() }; if remaining < BYTE - offset { acc += val >> (BYTE - offset - remaining); end_offset = remaining + offset; break; } else { acc += val << (remaining - (BYTE - offset)); remaining -= BYTE - offset; offset = 0; } } let (input, _) = input.peek_slice(cnt); Ok(((input, end_offset), acc)) } } } /// Parse taking `count` bits and comparing them to `pattern` /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::Bytes; /// # use winnow::error::{InputError, ErrorKind}; /// use winnow::binary::bits::tag; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// /// Compare the lowest `count` bits of `input` against the lowest `count` bits of `pattern`. /// /// Return Ok and the matching section of `input` if there's a match. /// /// Return Err if there's no match. /// fn parser(pattern: u8, count: u8, input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), u8> { /// tag(pattern, count).parse_peek(input) /// } /// /// // The lowest 4 bits of 0b00001111 match the lowest 4 bits of 0b11111111. /// assert_eq!( /// parser(0b0000_1111, 4, (stream(&[0b1111_1111]), 0)), /// Ok(((stream(&[0b1111_1111]), 4), 0b0000_1111)) /// ); /// /// // The lowest bit of 0b00001111 matches the lowest bit of 0b11111111 (both are 1). /// assert_eq!( /// parser(0b00000001, 1, (stream(&[0b11111111]), 0)), /// Ok(((stream(&[0b11111111]), 1), 0b00000001)) /// ); /// /// // The lowest 2 bits of 0b11111111 and 0b00000001 are different. /// assert_eq!( /// parser(0b000000_01, 2, (stream(&[0b111111_11]), 0)), /// Err(winnow::error::ErrMode::Backtrack(InputError::new( /// (stream(&[0b11111111]), 0), /// ErrorKind::Tag /// ))) /// ); /// /// // The lowest 8 bits of 0b11111111 and 0b11111110 are different. /// assert_eq!( /// parser(0b11111110, 8, (stream(&[0b11111111]), 0)), /// Err(winnow::error::ErrMode::Backtrack(InputError::new( /// (stream(&[0b11111111]), 0), /// ErrorKind::Tag /// ))) /// ); /// ``` #[inline(always)] #[doc(alias = "literal")] #[doc(alias = "just")] pub fn tag>( pattern: O, count: C, ) -> impl Parser<(I, usize), O, E> where I: Stream + AsBytes + StreamIsPartial + Clone, C: ToUsize, O: From + AddAssign + Shl + Shr + PartialEq, { let count = count.to_usize(); trace("tag", move |input: &mut (I, usize)| { let start = input.checkpoint(); take(count).parse_next(input).and_then(|o| { if pattern == o { Ok(o) } else { input.reset(start); Err(ErrMode::Backtrack(E::from_error_kind( input, ErrorKind::Tag, ))) } }) }) } /// Parses one specific bit as a bool. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::Bytes; /// # use winnow::error::{InputError, ErrorKind}; /// use winnow::binary::bits::bool; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parse(input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), bool> { /// bool.parse_peek(input) /// } /// /// assert_eq!(parse((stream(&[0b10000000]), 0)), Ok(((stream(&[0b10000000]), 1), true))); /// assert_eq!(parse((stream(&[0b10000000]), 1)), Ok(((stream(&[0b10000000]), 2), false))); /// ``` #[doc(alias = "any")] pub fn bool>(input: &mut (I, usize)) -> PResult where I: Stream + AsBytes + StreamIsPartial + Clone, { trace("bool", |input: &mut (I, usize)| { let bit: u32 = take(1usize).parse_next(input)?; Ok(bit != 0) }) .parse_next(input) } winnow-0.5.15/src/binary/bits/tests.rs000064400000000000000000000123451046102023000157660ustar 00000000000000use super::*; use crate::error::InputError; use crate::Partial; #[test] /// Take the `bits` function and assert that remaining bytes are correctly returned, if the /// previous bytes are fully consumed fn test_complete_byte_consumption_bits() { let input = &[0x12, 0x34, 0x56, 0x78][..]; // Take 3 bit slices with sizes [4, 8, 4]. let result: IResult<&[u8], (u8, u8, u8)> = bits::<_, _, InputError<(&[u8], usize)>, _, _>((take(4usize), take(8usize), take(4usize))) .parse_peek(input); let output = result.expect("We take 2 bytes and the input is longer than 2 bytes"); let remaining = output.0; assert_eq!(remaining, [0x56, 0x78]); let parsed = output.1; assert_eq!(parsed.0, 0x01); assert_eq!(parsed.1, 0x23); assert_eq!(parsed.2, 0x04); } #[test] /// Take the `bits` function and assert that remaining bytes are correctly returned, if the /// previous bytes are NOT fully consumed. Partially consumed bytes are supposed to be dropped. /// I.e. if we consume 1.5 bytes of 4 bytes, 2 bytes will be returned, bits 13-16 will be /// dropped. fn test_partial_byte_consumption_bits() { let input = &[0x12, 0x34, 0x56, 0x78][..]; // Take bit slices with sizes [4, 8]. let result: IResult<&[u8], (u8, u8)> = bits::<_, _, InputError<(&[u8], usize)>, _, _>((take(4usize), take(8usize))) .parse_peek(input); let output = result.expect("We take 1.5 bytes and the input is longer than 2 bytes"); let remaining = output.0; assert_eq!(remaining, [0x56, 0x78]); let parsed = output.1; assert_eq!(parsed.0, 0x01); assert_eq!(parsed.1, 0x23); } #[test] #[cfg(feature = "std")] /// Ensure that in Incomplete error is thrown, if too few bytes are passed for a given parser. fn test_incomplete_bits() { let input = Partial::new(&[0x12][..]); // Take bit slices with sizes [4, 8]. let result: IResult<_, (u8, u8)> = bits::<_, _, InputError<(_, usize)>, _, _>((take(4usize), take(8usize))).parse_peek(input); assert!(result.is_err()); let error = result.err().unwrap(); assert_eq!("Parsing requires 2 bytes/chars", error.to_string()); } #[test] fn test_take_complete_0() { let input = &[0b00010010][..]; let count = 0usize; assert_eq!(count, 0usize); let offset = 0usize; let result: crate::IResult<(&[u8], usize), usize> = take(count).parse_peek((input, offset)); assert_eq!(result, Ok(((input, offset), 0))); } #[test] fn test_take_complete_eof() { let input = &[0b00010010][..]; let result: crate::IResult<(&[u8], usize), usize> = take(1usize).parse_peek((input, 8)); assert_eq!( result, Err(crate::error::ErrMode::Backtrack(InputError::new( (input, 8), ErrorKind::Eof ))) ); } #[test] fn test_take_complete_span_over_multiple_bytes() { let input = &[0b00010010, 0b00110100, 0b11111111, 0b11111111][..]; let result: crate::IResult<(&[u8], usize), usize> = take(24usize).parse_peek((input, 4)); assert_eq!( result, Ok((([0b11111111].as_ref(), 4), 0b1000110100111111111111)) ); } #[test] fn test_take_partial_0() { let input = Partial::new(&[][..]); let count = 0usize; assert_eq!(count, 0usize); let offset = 0usize; let result: crate::IResult<(_, usize), usize> = take(count).parse_peek((input, offset)); assert_eq!(result, Ok(((input, offset), 0))); } #[test] fn test_tag_partial_ok() { let input = Partial::new(&[0b00011111][..]); let offset = 0usize; let bits_to_take = 4usize; let value_to_tag = 0b0001; let result: crate::IResult<(_, usize), usize> = tag(value_to_tag, bits_to_take).parse_peek((input, offset)); assert_eq!(result, Ok(((input, bits_to_take), value_to_tag))); } #[test] fn test_tag_partial_err() { let input = Partial::new(&[0b00011111][..]); let offset = 0usize; let bits_to_take = 4usize; let value_to_tag = 0b1111; let result: crate::IResult<(_, usize), usize> = tag(value_to_tag, bits_to_take).parse_peek((input, offset)); assert_eq!( result, Err(crate::error::ErrMode::Backtrack(InputError::new( (input, offset), ErrorKind::Tag ))) ); } #[test] fn test_bool_0_complete() { let input = [0b10000000].as_ref(); let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 0)); assert_eq!(result, Ok(((input, 1), true))); } #[test] fn test_bool_eof_complete() { let input = [0b10000000].as_ref(); let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 8)); assert_eq!( result, Err(crate::error::ErrMode::Backtrack(InputError::new( (input, 8), ErrorKind::Eof ))) ); } #[test] fn test_bool_0_partial() { let input = Partial::new([0b10000000].as_ref()); let result: crate::IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 0)); assert_eq!(result, Ok(((input, 1), true))); } #[test] fn test_bool_eof_partial() { let input = Partial::new([0b10000000].as_ref()); let result: crate::IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 8)); assert_eq!( result, Err(crate::error::ErrMode::Incomplete(Needed::new(1))) ); } winnow-0.5.15/src/binary/mod.rs000064400000000000000000002552241046102023000144470ustar 00000000000000//! Parsers recognizing numbers #![allow(clippy::match_same_arms)] pub mod bits; #[cfg(test)] mod tests; use crate::combinator::repeat; use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::Needed; use crate::error::ParserError; use crate::lib::std::ops::{Add, Shl}; use crate::stream::Accumulate; use crate::stream::{AsBytes, Stream, StreamIsPartial}; use crate::stream::{ToUsize, UpdateSlice}; use crate::token::take; use crate::trace::trace; use crate::PResult; use crate::Parser; /// Configurable endianness #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Endianness { /// Big endian Big, /// Little endian Little, /// Will match the host's endianness Native, } /// Recognizes an unsigned 1 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_u8; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u8> { /// be_u8.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); /// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_u8; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, { u8(input) } /// Recognizes a big endian unsigned 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_u16; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u16> { /// be_u16.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_u16; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_u16", move |input: &mut I| be_uint(input, 2)).parse_next(input) } /// Recognizes a big endian unsigned 3 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_u24; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u32> { /// be_u24.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_u24; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_u23", move |input: &mut I| be_uint(input, 3)).parse_next(input) } /// Recognizes a big endian unsigned 4 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_u32; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u32> { /// be_u32.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_u32; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_u32", move |input: &mut I| be_uint(input, 4)).parse_next(input) } /// Recognizes a big endian unsigned 8 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_u64; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u64> { /// be_u64.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_u64; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_u64", move |input: &mut I| be_uint(input, 8)).parse_next(input) } /// Recognizes a big endian unsigned 16 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_u128; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u128> { /// be_u128.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_u128; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_u128", move |input: &mut I| be_uint(input, 16)).parse_next(input) } #[inline] fn be_uint>(input: &mut I, bound: usize) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, Uint: Default + Shl + Add + From, { debug_assert_ne!(bound, 1, "to_be_uint needs extra work to avoid overflow"); take(bound) .map(|n: ::Slice| to_be_uint(n.as_bytes())) .parse_next(input) } #[inline] fn to_be_uint(number: &[u8]) -> Uint where Uint: Default + Shl + Add + From, { let mut res = Uint::default(); for byte in number.iter().copied() { res = (res << 8) + byte.into(); } res } /// Recognizes a signed 1 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_i8; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i8> { /// be_i8.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); /// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_i8; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, { i8(input) } /// Recognizes a big endian signed 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_i16; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i16> { /// be_i16.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_i16; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_i16", move |input: &mut I| { be_uint::<_, u16, _>(input, 2).map(|n| n as i16) }) .parse_next(input) } /// Recognizes a big endian signed 3 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_i24; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i32> { /// be_i24.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_i24; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_i24", move |input: &mut I| { be_uint::<_, u32, _>(input, 3).map(|n| { // Same as the unsigned version but we need to sign-extend manually here let n = if n & 0x80_00_00 != 0 { (n | 0xff_00_00_00) as i32 } else { n as i32 }; n }) }) .parse_next(input) } /// Recognizes a big endian signed 4 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_i32; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i32> { /// be_i32.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_i32; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_i32", move |input: &mut I| { be_uint::<_, u32, _>(input, 4).map(|n| n as i32) }) .parse_next(input) } /// Recognizes a big endian signed 8 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_i64; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i64> { /// be_i64.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_i64; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_i64", move |input: &mut I| { be_uint::<_, u64, _>(input, 8).map(|n| n as i64) }) .parse_next(input) } /// Recognizes a big endian signed 16 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_i128; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i128> { /// be_i128.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_i128; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_i128", move |input: &mut I| { be_uint::<_, u128, _>(input, 16).map(|n| n as i128) }) .parse_next(input) } /// Recognizes an unsigned 1 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_u8; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u8> { /// le_u8.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); /// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_u8; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, { u8(input) } /// Recognizes a little endian unsigned 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_u16; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u16> { /// le_u16.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_u16; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_u16", move |input: &mut I| le_uint(input, 2)).parse_next(input) } /// Recognizes a little endian unsigned 3 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_u24; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u32> { /// le_u24.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_u24; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_u24", move |input: &mut I| le_uint(input, 3)).parse_next(input) } /// Recognizes a little endian unsigned 4 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_u32; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u32> { /// le_u32.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_u32; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_u32", move |input: &mut I| le_uint(input, 4)).parse_next(input) } /// Recognizes a little endian unsigned 8 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_u64; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u64> { /// le_u64.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_u64; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_u64", move |input: &mut I| le_uint(input, 8)).parse_next(input) } /// Recognizes a little endian unsigned 16 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_u128; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u128> { /// le_u128.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_u128; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_u128", move |input: &mut I| le_uint(input, 16)).parse_next(input) } #[inline] fn le_uint>(input: &mut I, bound: usize) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, Uint: Default + Shl + Add + From, { take(bound) .map(|n: ::Slice| to_le_uint(n.as_bytes())) .parse_next(input) } #[inline] fn to_le_uint(number: &[u8]) -> Uint where Uint: Default + Shl + Add + From, { let mut res = Uint::default(); for (index, byte) in number.iter_offsets() { res = res + (Uint::from(byte) << (8 * index as u8)); } res } /// Recognizes a signed 1 byte integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_i8; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i8> { /// le_i8.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); /// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_i8; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, { i8(input) } /// Recognizes a little endian signed 2 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_i16; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i16> { /// le_i16.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_i16; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_i16", move |input: &mut I| { le_uint::<_, u16, _>(input, 2).map(|n| n as i16) }) .parse_next(input) } /// Recognizes a little endian signed 3 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_i24; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i32> { /// le_i24.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_i24; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_i24", move |input: &mut I| { le_uint::<_, u32, _>(input, 3).map(|n| { // Same as the unsigned version but we need to sign-extend manually here let n = if n & 0x80_00_00 != 0 { (n | 0xff_00_00_00) as i32 } else { n as i32 }; n }) }) .parse_next(input) } /// Recognizes a little endian signed 4 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_i32; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i32> { /// le_i32.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_i32; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_i32", move |input: &mut I| { le_uint::<_, u32, _>(input, 4).map(|n| n as i32) }) .parse_next(input) } /// Recognizes a little endian signed 8 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_i64; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i64> { /// le_i64.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_i64; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_i64", move |input: &mut I| { le_uint::<_, u64, _>(input, 8).map(|n| n as i64) }) .parse_next(input) } /// Recognizes a little endian signed 16 bytes integer. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_i128; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i128> { /// le_i128.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); /// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_i128; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_i128", move |input: &mut I| { le_uint::<_, u128, _>(input, 16).map(|n| n as i128) }) .parse_next(input) } /// Recognizes an unsigned 1 byte integer /// /// **Note:** that endianness does not apply to 1 byte numbers. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::u8; /// /// fn parser(s: &[u8]) -> IResult<&[u8], u8> { /// u8.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); /// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::u8; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, { trace("u8", move |input: &mut I| { if ::is_partial_supported() { u8_::<_, _, true>(input) } else { u8_::<_, _, false>(input) } }) .parse_next(input) } fn u8_, const PARTIAL: bool>(input: &mut I) -> PResult where I: StreamIsPartial, I: Stream, { input.next_token().ok_or_else(|| { if PARTIAL && input.is_partial() { ErrMode::Incomplete(Needed::new(1)) } else { ErrMode::Backtrack(E::from_error_kind(input, ErrorKind::Token)) } }) } /// Recognizes an unsigned 2 bytes integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u16 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian u16 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::u16; /// /// let be_u16 = |s| { /// u16(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003))); /// assert_eq!(be_u16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_u16 = |s| { /// u16(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300))); /// assert_eq!(le_u16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::u16; /// /// let be_u16 = |s| { /// u16::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0003))); /// assert_eq!(be_u16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); /// /// let le_u16 = |s| { /// u16::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0300))); /// assert_eq!(le_u16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn u16>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_u16, Endianness::Little => le_u16, #[cfg(target_endian = "big")] Endianness::Native => be_u16, #[cfg(target_endian = "little")] Endianness::Native => le_u16, } }(input) } /// Recognizes an unsigned 3 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u24 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian u24 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::u24; /// /// let be_u24 = |s| { /// u24(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); /// assert_eq!(be_u24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_u24 = |s| { /// u24(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); /// assert_eq!(le_u24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::u24; /// /// let be_u24 = |s| { /// u24::<_,InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x000305))); /// assert_eq!(be_u24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2)))); /// /// let le_u24 = |s| { /// u24::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x050300))); /// assert_eq!(le_u24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2)))); /// ``` #[inline(always)] pub fn u24>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_u24, Endianness::Little => le_u24, #[cfg(target_endian = "big")] Endianness::Native => be_u24, #[cfg(target_endian = "little")] Endianness::Native => le_u24, } }(input) } /// Recognizes an unsigned 4 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u32 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian u32 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::u32; /// /// let be_u32 = |s| { /// u32(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); /// assert_eq!(be_u32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_u32 = |s| { /// u32(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); /// assert_eq!(le_u32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::u32; /// /// let be_u32 = |s| { /// u32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00030507))); /// assert_eq!(be_u32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3)))); /// /// let le_u32 = |s| { /// u32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07050300))); /// assert_eq!(le_u32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3)))); /// ``` #[inline(always)] pub fn u32>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_u32, Endianness::Little => le_u32, #[cfg(target_endian = "big")] Endianness::Native => be_u32, #[cfg(target_endian = "little")] Endianness::Native => le_u32, } }(input) } /// Recognizes an unsigned 8 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u64 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian u64 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::u64; /// /// let be_u64 = |s| { /// u64(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); /// assert_eq!(be_u64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_u64 = |s| { /// u64(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); /// assert_eq!(le_u64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::u64; /// /// let be_u64 = |s| { /// u64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0001020304050607))); /// assert_eq!(be_u64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7)))); /// /// let le_u64 = |s| { /// u64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0706050403020100))); /// assert_eq!(le_u64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7)))); /// ``` #[inline(always)] pub fn u64>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_u64, Endianness::Little => le_u64, #[cfg(target_endian = "big")] Endianness::Native => be_u64, #[cfg(target_endian = "little")] Endianness::Native => le_u64, } }(input) } /// Recognizes an unsigned 16 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u128 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian u128 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::u128; /// /// let be_u128 = |s| { /// u128(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); /// assert_eq!(be_u128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_u128 = |s| { /// u128(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); /// assert_eq!(le_u128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::u128; /// /// let be_u128 = |s| { /// u128::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_u128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00010203040506070001020304050607))); /// assert_eq!(be_u128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15)))); /// /// let le_u128 = |s| { /// u128::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_u128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07060504030201000706050403020100))); /// assert_eq!(le_u128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15)))); /// ``` #[inline(always)] pub fn u128>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_u128, Endianness::Little => le_u128, #[cfg(target_endian = "big")] Endianness::Native => be_u128, #[cfg(target_endian = "little")] Endianness::Native => le_u128, } }(input) } /// Recognizes a signed 1 byte integer /// /// **Note:** that endianness does not apply to 1 byte numbers. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::i8; /// /// fn parser(s: &[u8]) -> IResult<&[u8], i8> { /// i8.parse_peek(s) /// } /// /// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00))); /// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::i8; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, { trace("i8", move |input: &mut I| { 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*: 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 I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_i16, Endianness::Little => le_i16, #[cfg(target_endian = "big")] Endianness::Native => be_i16, #[cfg(target_endian = "little")] Endianness::Native => le_i16, } }(input) } /// Recognizes a signed 3 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i24 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian i24 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::i24; /// /// let be_i24 = |s| { /// i24(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305))); /// assert_eq!(be_i24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_i24 = |s| { /// i24(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300))); /// assert_eq!(le_i24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::i24; /// /// let be_i24 = |s| { /// i24::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x000305))); /// assert_eq!(be_i24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2)))); /// /// let le_i24 = |s| { /// i24::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x050300))); /// assert_eq!(le_i24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2)))); /// ``` #[inline(always)] pub fn i24>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_i24, Endianness::Little => le_i24, #[cfg(target_endian = "big")] Endianness::Native => be_i24, #[cfg(target_endian = "little")] Endianness::Native => le_i24, } }(input) } /// Recognizes a signed 4 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i32 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian i32 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::i32; /// /// let be_i32 = |s| { /// i32(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507))); /// assert_eq!(be_i32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_i32 = |s| { /// i32(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300))); /// assert_eq!(le_i32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::i32; /// /// let be_i32 = |s| { /// i32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00030507))); /// assert_eq!(be_i32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3)))); /// /// let le_i32 = |s| { /// i32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07050300))); /// assert_eq!(le_i32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3)))); /// ``` #[inline(always)] pub fn i32>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_i32, Endianness::Little => le_i32, #[cfg(target_endian = "big")] Endianness::Native => be_i32, #[cfg(target_endian = "little")] Endianness::Native => le_i32, } }(input) } /// Recognizes a signed 8 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i64 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian i64 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::i64; /// /// let be_i64 = |s| { /// i64(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607))); /// assert_eq!(be_i64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_i64 = |s| { /// i64(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100))); /// assert_eq!(le_i64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::i64; /// /// let be_i64 = |s| { /// i64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0001020304050607))); /// assert_eq!(be_i64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7)))); /// /// let le_i64 = |s| { /// i64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0706050403020100))); /// assert_eq!(le_i64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7)))); /// ``` #[inline(always)] pub fn i64>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_i64, Endianness::Little => le_i64, #[cfg(target_endian = "big")] Endianness::Native => be_i64, #[cfg(target_endian = "little")] Endianness::Native => le_i64, } }(input) } /// Recognizes a signed 16 byte integer /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i128 integer, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian i128 integer. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::i128; /// /// let be_i128 = |s| { /// i128(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607))); /// assert_eq!(be_i128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// /// let le_i128 = |s| { /// i128(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100))); /// assert_eq!(le_i128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::i128; /// /// let be_i128 = |s| { /// i128::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_i128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00010203040506070001020304050607))); /// assert_eq!(be_i128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15)))); /// /// let le_i128 = |s| { /// i128::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_i128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07060504030201000706050403020100))); /// assert_eq!(le_i128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15)))); /// ``` #[inline(always)] pub fn i128>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_i128, Endianness::Little => le_i128, #[cfg(target_endian = "big")] Endianness::Native => be_i128, #[cfg(target_endian = "little")] Endianness::Native => le_i128, } }(input) } /// Recognizes a big endian 4 bytes floating point number. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_f32; /// /// fn parser(s: &[u8]) -> IResult<&[u8], f32> { /// be_f32.parse_peek(s) /// } /// /// assert_eq!(parser(&[0x41, 0x48, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); /// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_f32; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_f32", move |input: &mut I| { be_uint::<_, u32, _>(input, 4).map(f32::from_bits) }) .parse_next(input) } /// Recognizes a big endian 8 bytes floating point number. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::be_f64; /// /// fn parser(s: &[u8]) -> IResult<&[u8], f64> { /// be_f64.parse_peek(s) /// } /// /// assert_eq!(parser(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); /// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::be_f64; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_f64", move |input: &mut I| { be_uint::<_, u64, _>(input, 8).map(f64::from_bits) }) .parse_next(input) } /// Recognizes a little endian 4 bytes floating point number. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_f32; /// /// fn parser(s: &[u8]) -> IResult<&[u8], f32> { /// le_f32.parse_peek(s) /// } /// /// assert_eq!(parser(&[0x00, 0x00, 0x48, 0x41][..]), Ok((&b""[..], 12.5))); /// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_f32; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("le_f32", move |input: &mut I| { le_uint::<_, u32, _>(input, 4).map(f32::from_bits) }) .parse_next(input) } /// Recognizes a little endian 8 bytes floating point number. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::le_f64; /// /// fn parser(s: &[u8]) -> IResult<&[u8], f64> { /// le_f64.parse_peek(s) /// } /// /// assert_eq!(parser(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..]), Ok((&b""[..], 12.5))); /// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::binary::le_f64; /// /// fn parser(s: Partial<&[u8]>) -> IResult, 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 I) -> PResult where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { trace("be_f64", move |input: &mut I| { le_uint::<_, u64, _>(input, 8).map(f64::from_bits) }) .parse_next(input) } /// Recognizes a 4 byte floating point number /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian f32 float, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian f32 float. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::f32; /// /// let be_f32 = |s| { /// f32(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_f32(&[0x41, 0x48, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); /// assert_eq!(be_f32(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// /// let le_f32 = |s| { /// f32(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_f32(&[0x00, 0x00, 0x48, 0x41][..]), Ok((&b""[..], 12.5))); /// assert_eq!(le_f32(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::f32; /// /// let be_f32 = |s| { /// f32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_f32(Partial::new(&[0x41, 0x48, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5))); /// assert_eq!(be_f32(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); /// /// let le_f32 = |s| { /// f32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_f32(Partial::new(&[0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 12.5))); /// assert_eq!(le_f32(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn f32>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_f32, Endianness::Little => le_f32, #[cfg(target_endian = "big")] Endianness::Native => be_f32, #[cfg(target_endian = "little")] Endianness::Native => le_f32, } }(input) } /// Recognizes an 8 byte floating point number /// /// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian f64 float, /// otherwise if `winnow::binary::Endianness::Little` parse a little endian f64 float. /// /// *Complete version*: returns an error if there is not enough input data /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::binary::f64; /// /// let be_f64 = |s| { /// f64(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_f64(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((&b""[..], 12.5))); /// assert_eq!(be_f64(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// /// let le_f64 = |s| { /// f64(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..]), Ok((&b""[..], 12.5))); /// assert_eq!(le_f64(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// # use winnow::Partial; /// use winnow::binary::f64; /// /// let be_f64 = |s| { /// f64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s) /// }; /// /// assert_eq!(be_f64(Partial::new(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5))); /// assert_eq!(be_f64(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(5)))); /// /// let le_f64 = |s| { /// f64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s) /// }; /// /// assert_eq!(le_f64(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..])), Ok((Partial::new(&b""[..]), 12.5))); /// assert_eq!(le_f64(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(5)))); /// ``` #[inline(always)] pub fn f64>(endian: Endianness) -> impl Parser where I: StreamIsPartial, I: Stream, ::Slice: AsBytes, { move |input: &mut I| { match endian { Endianness::Big => be_f64, Endianness::Little => le_f64, #[cfg(target_endian = "big")] Endianness::Native => be_f64, #[cfg(target_endian = "little")] Endianness::Native => le_f64, } }(input) } /// Gets a number from the parser and returns a /// subslice of the input of that size. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Arguments /// * `f` The parser to apply. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed, stream::Partial}; /// # use winnow::prelude::*; /// use winnow::Bytes; /// use winnow::binary::be_u16; /// use winnow::binary::length_data; /// use winnow::token::tag; /// /// type Stream<'i> = Partial<&'i Bytes>; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Partial::new(Bytes::new(b)) /// } /// /// fn parser(s: Stream<'_>) -> IResult, &[u8]> { /// length_data(be_u16).parse_peek(s) /// } /// /// assert_eq!(parser(stream(b"\x00\x03abcefg")), Ok((stream(&b"efg"[..]), &b"abc"[..]))); /// assert_eq!(parser(stream(b"\x00\x03a")), Err(ErrMode::Incomplete(Needed::new(2)))); /// ``` pub fn length_data(mut f: F) -> impl Parser::Slice, E> where I: StreamIsPartial, I: Stream, N: ToUsize, F: Parser, E: ParserError, { trace("length_data", move |i: &mut I| { let length = f.parse_next(i)?; crate::token::take(length).parse_next(i) }) } /// Gets a number from the first parser, /// takes a subslice of the input of that size, /// then applies the second parser on that subslice. /// If the second parser returns `Incomplete`, /// `length_value` will return an error. /// /// *Complete version*: Returns an error if there is not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data. /// /// # Arguments /// * `f` The parser to apply. /// * `g` The parser to apply on the subslice. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed, stream::{Partial, StreamIsPartial}}; /// # use winnow::prelude::*; /// use winnow::Bytes; /// use winnow::binary::be_u16; /// use winnow::binary::length_value; /// use winnow::token::tag; /// /// type Stream<'i> = Partial<&'i Bytes>; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Partial::new(Bytes::new(b)) /// } /// /// fn complete_stream(b: &[u8]) -> Stream<'_> { /// let mut p = Partial::new(Bytes::new(b)); /// let _ = p.complete(); /// p /// } /// /// fn parser(s: Stream<'_>) -> IResult, &[u8]> { /// length_value(be_u16, "abc").parse_peek(s) /// } /// /// assert_eq!(parser(stream(b"\x00\x03abcefg")), Ok((stream(&b"efg"[..]), &b"abc"[..]))); /// assert_eq!(parser(stream(b"\x00\x03123123")), Err(ErrMode::Backtrack(InputError::new(complete_stream(&b"123"[..]), ErrorKind::Tag)))); /// assert_eq!(parser(stream(b"\x00\x03a")), Err(ErrMode::Incomplete(Needed::new(2)))); /// ``` pub fn length_value(mut f: F, mut g: G) -> impl Parser where I: StreamIsPartial, I: Stream + UpdateSlice + Clone, N: ToUsize, F: Parser, G: Parser, E: ParserError, { trace("length_value", move |i: &mut I| { let data = length_data(f.by_ref()).parse_next(i)?; let mut data = I::update_slice(i.clone(), data); let _ = data.complete(); let o = g.by_ref().complete_err().parse_next(&mut data)?; Ok(o) }) } /// Gets a number from the first parser, /// then applies the second parser that many times. /// /// # Arguments /// * `f` The parser to apply to obtain the count. /// * `g` The parser to apply repeatedly. /// /// # Example /// /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::Bytes; /// use winnow::binary::u8; /// use winnow::binary::length_count; /// use winnow::token::tag; /// /// type Stream<'i> = &'i Bytes; /// /// fn stream(b: &[u8]) -> Stream<'_> { /// Bytes::new(b) /// } /// /// fn parser(s: Stream<'_>) -> IResult, Vec<&[u8]>> { /// length_count(u8.map(|i| { /// println!("got number: {}", i); /// i /// }), "abc").parse_peek(s) /// } /// /// assert_eq!(parser(stream(b"\x02abcabcabc")), Ok((stream(b"abc"), vec![&b"abc"[..], &b"abc"[..]]))); /// assert_eq!(parser(stream(b"\x03123123123")), Err(ErrMode::Backtrack(InputError::new(stream(b"123123123"), ErrorKind::Tag)))); /// # } /// ``` pub fn length_count(mut f: F, mut g: G) -> impl Parser where I: Stream, N: ToUsize, C: Accumulate, F: Parser, G: Parser, E: ParserError, { trace("length_count", move |i: &mut I| { let n = f.parse_next(i)?; let n = n.to_usize(); repeat(n, g.by_ref()).parse_next(i) }) } winnow-0.5.15/src/binary/tests.rs000064400000000000000000001204621046102023000150250ustar 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_i8_tests() { assert_parse!( le_i8.parse_peek(Partial::new(&[0x00][..])), Ok((Partial::new(&b""[..]), 0)) ); assert_parse!( le_i8.parse_peek(Partial::new(&[0x7f][..])), Ok((Partial::new(&b""[..]), 127)) ); assert_parse!( le_i8.parse_peek(Partial::new(&[0xff][..])), Ok((Partial::new(&b""[..]), -1)) ); assert_parse!( le_i8.parse_peek(Partial::new(&[0x80][..])), Ok((Partial::new(&b""[..]), -128)) ); } #[test] fn le_i16_tests() { assert_parse!( le_i16.parse_peek(Partial::new(&[0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 0)) ); assert_parse!( le_i16.parse_peek(Partial::new(&[0xff, 0x7f][..])), Ok((Partial::new(&b""[..]), 32_767_i16)) ); assert_parse!( le_i16.parse_peek(Partial::new(&[0xff, 0xff][..])), Ok((Partial::new(&b""[..]), -1)) ); assert_parse!( le_i16.parse_peek(Partial::new(&[0x00, 0x80][..])), Ok((Partial::new(&b""[..]), -32_768_i16)) ); } #[test] fn le_u24_tests() { assert_parse!( le_u24.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 0)) ); assert_parse!( le_u24.parse_peek(Partial::new(&[0xFF, 0xFF, 0x00][..])), Ok((Partial::new(&b""[..]), 65_535_u32)) ); assert_parse!( le_u24.parse_peek(Partial::new(&[0x56, 0x34, 0x12][..])), Ok((Partial::new(&b""[..]), 1_193_046_u32)) ); } #[test] fn le_i24_tests() { assert_parse!( le_i24.parse_peek(Partial::new(&[0xFF, 0xFF, 0xFF][..])), Ok((Partial::new(&b""[..]), -1_i32)) ); assert_parse!( le_i24.parse_peek(Partial::new(&[0x00, 0x00, 0xFF][..])), Ok((Partial::new(&b""[..]), -65_536_i32)) ); assert_parse!( le_i24.parse_peek(Partial::new(&[0xAA, 0xCB, 0xED][..])), Ok((Partial::new(&b""[..]), -1_193_046_i32)) ); } #[test] fn le_i32_tests() { assert_parse!( le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 0)) ); assert_parse!( le_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0x7f][..])), Ok((Partial::new(&b""[..]), 2_147_483_647_i32)) ); assert_parse!( le_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0xff][..])), Ok((Partial::new(&b""[..]), -1)) ); assert_parse!( le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x80][..])), Ok((Partial::new(&b""[..]), -2_147_483_648_i32)) ); } #[test] fn le_i64_tests() { assert_parse!( le_i64.parse_peek(Partial::new( &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] )), Ok((Partial::new(&b""[..]), 0)) ); assert_parse!( le_i64.parse_peek(Partial::new( &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f][..] )), Ok((Partial::new(&b""[..]), 9_223_372_036_854_775_807_i64)) ); assert_parse!( le_i64.parse_peek(Partial::new( &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..] )), Ok((Partial::new(&b""[..]), -1)) ); assert_parse!( le_i64.parse_peek(Partial::new( &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80][..] )), Ok((Partial::new(&b""[..]), -9_223_372_036_854_775_808_i64)) ); } #[test] fn le_i128_tests() { assert_parse!( le_i128.parse_peek(Partial::new( &[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ][..] )), Ok((Partial::new(&b""[..]), 0)) ); assert_parse!( le_i128.parse_peek(Partial::new( &[ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ][..] )), Ok(( Partial::new(&b""[..]), 170_141_183_460_469_231_731_687_303_715_884_105_727_i128 )) ); assert_parse!( le_i128.parse_peek(Partial::new( &[ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ][..] )), Ok((Partial::new(&b""[..]), -1)) ); assert_parse!( le_i128.parse_peek(Partial::new( &[ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 ][..] )), Ok(( Partial::new(&b""[..]), -170_141_183_460_469_231_731_687_303_715_884_105_728_i128 )) ); } #[test] fn be_f32_tests() { assert_parse!( be_f32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 0_f32)) ); assert_parse!( be_f32.parse_peek(Partial::new(&[0x4d, 0x31, 0x1f, 0xd8][..])), Ok((Partial::new(&b""[..]), 185_728_380_f32)) ); } #[test] fn be_f64_tests() { assert_parse!( be_f64.parse_peek(Partial::new( &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] )), Ok((Partial::new(&b""[..]), 0_f64)) ); assert_parse!( be_f64.parse_peek(Partial::new( &[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00][..] )), Ok((Partial::new(&b""[..]), 185_728_392_f64)) ); } #[test] fn le_f32_tests() { assert_parse!( le_f32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 0_f32)) ); assert_parse!( le_f32.parse_peek(Partial::new(&[0xd8, 0x1f, 0x31, 0x4d][..])), Ok((Partial::new(&b""[..]), 185_728_380_f32)) ); } #[test] fn le_f64_tests() { assert_parse!( le_f64.parse_peek(Partial::new( &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..] )), Ok((Partial::new(&b""[..]), 0_f64)) ); assert_parse!( le_f64.parse_peek(Partial::new( &[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41][..] )), Ok((Partial::new(&b""[..]), 185_728_392_f64)) ); } #[test] fn configurable_endianness() { use crate::binary::Endianness; fn be_tst16(i: Partial<&[u8]>) -> IResult, 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_count_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_count(unpeek(number), "abc").parse_peek(i) } assert_eq!( cnt(Partial::new(&b"2abcabcabcdef"[..])), Ok((Partial::new(&b"abcdef"[..]), vec![&b"abc"[..], &b"abc"[..]])) ); assert_eq!( cnt(Partial::new(&b"2ab"[..])), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( cnt(Partial::new(&b"3abcab"[..])), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( cnt(Partial::new(&b"xxx"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"xxx"[..]), ErrorKind::Slice ))) ); assert_eq!( cnt(Partial::new(&b"2abcxxx"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"xxx"[..]), ErrorKind::Tag ))) ); } #[test] fn length_data_test() { fn number(i: Partial<&[u8]>) -> IResult, u32> { digit .try_map(str::from_utf8) .try_map(FromStr::from_str) .parse_peek(i) } fn take(i: Partial<&[u8]>) -> IResult, &[u8]> { length_data(unpeek(number)).parse_peek(i) } assert_eq!( take(Partial::new(&b"6abcabcabcdef"[..])), Ok((Partial::new(&b"abcdef"[..]), &b"abcabc"[..])) ); assert_eq!( take(Partial::new(&b"3ab"[..])), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( take(Partial::new(&b"xxx"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"xxx"[..]), ErrorKind::Slice ))) ); assert_eq!( take(Partial::new(&b"2abcxxx"[..])), Ok((Partial::new(&b"cxxx"[..]), &b"ab"[..])) ); } #[test] fn length_value_test() { use crate::stream::StreamIsPartial; fn length_value_1(i: Partial<&[u8]>) -> IResult, u16> { length_value(be_u8, be_u16).parse_peek(i) } fn length_value_2(i: Partial<&[u8]>) -> IResult, (u8, u8)> { length_value(be_u8, (be_u8, be_u8)).parse_peek(i) } let mut empty_complete = Partial::new(&b""[..]); let _ = empty_complete.complete(); let i1 = [0, 5, 6]; assert_eq!( length_value_1(Partial::new(&i1)), Err(ErrMode::Backtrack(error_position!( &empty_complete, ErrorKind::Slice ))) ); assert_eq!( length_value_2(Partial::new(&i1)), Err(ErrMode::Backtrack(error_position!( &empty_complete, ErrorKind::Token ))) ); let i2 = [1, 5, 6, 3]; { let mut middle_complete = Partial::new(&i2[1..2]); let _ = middle_complete.complete(); assert_eq!( length_value_1(Partial::new(&i2)), Err(ErrMode::Backtrack(error_position!( &middle_complete, ErrorKind::Slice ))) ); assert_eq!( length_value_2(Partial::new(&i2)), Err(ErrMode::Backtrack(error_position!( &empty_complete, ErrorKind::Token ))) ); } let i3 = [2, 5, 6, 3, 4, 5, 7]; assert_eq!( length_value_1(Partial::new(&i3)), Ok((Partial::new(&i3[3..]), 1286)) ); assert_eq!( length_value_2(Partial::new(&i3)), Ok((Partial::new(&i3[3..]), (5, 6))) ); let i4 = [3, 5, 6, 3, 4, 5]; assert_eq!( length_value_1(Partial::new(&i4)), Ok((Partial::new(&i4[4..]), 1286)) ); assert_eq!( length_value_2(Partial::new(&i4)), Ok((Partial::new(&i4[4..]), (5, 6))) ); } } winnow-0.5.15/src/combinator/branch.rs000064400000000000000000000225201046102023000157650ustar 00000000000000use crate::error::{ErrMode, ErrorKind, ParserError}; use crate::stream::Stream; use crate::trace::trace; use crate::*; #[doc(inline)] pub use crate::dispatch; /// Helper trait for the [alt()] combinator. /// /// This trait is implemented for tuples of up to 21 elements pub trait Alt { /// 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 /// /// For tight control over the error, add a final case using [`fail`][crate::combinator::fail]. /// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all /// errors or return the error of the parser that went the farthest in the input data. /// /// When the alternative cases have unique prefixes, [`dispatch`] can offer better performance. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::InputError,error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::ascii::{alpha1, digit1}; /// use winnow::combinator::alt; /// # fn main() { /// fn parser(input: &str) -> IResult<&str, &str> { /// alt((alpha1, digit1)).parse_peek(input) /// }; /// /// // the first parser, alpha1, recognizes the input /// assert_eq!(parser("abc"), Ok(("", "abc"))); /// /// // the first parser returns an error, so alt tries the second one /// assert_eq!(parser("123456"), Ok(("", "123456"))); /// /// // both parsers failed, and with the default error type, alt will return the last error /// assert_eq!(parser(" "), Err(ErrMode::Backtrack(InputError::new(" ", ErrorKind::Slice)))); /// # } /// ``` #[doc(alias = "choice")] pub fn alt, List: Alt>( mut l: List, ) -> impl Parser { trace("alt", move |i: &mut I| l.choice(i)) } /// Helper trait for the [permutation()] combinator. /// /// This trait is implemented for tuples of up to 21 elements pub trait Permutation { /// 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. /// /// ```rust /// # use winnow::{error::ErrMode,error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::ascii::{alpha1, digit1}; /// use winnow::combinator::permutation; /// # fn main() { /// fn parser(input: &str) -> IResult<&str, (&str, &str)> { /// permutation((alpha1, digit1)).parse_peek(input) /// } /// /// // permutation recognizes alphabetic characters then digit /// assert_eq!(parser("abc123"), Ok(("", ("abc", "123")))); /// /// // but also in inverse order /// assert_eq!(parser("123abc"), Ok(("", ("abc", "123")))); /// /// // it will fail if one of the parsers failed /// assert_eq!(parser("abc;"), Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Slice)))); /// # } /// ``` /// /// The parsers are applied greedily: if there are multiple unapplied parsers /// that could parse the next slice of input, the first one is used. /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; /// # use winnow::prelude::*; /// use winnow::combinator::permutation; /// use winnow::token::any; /// /// fn parser(input: &str) -> IResult<&str, (char, char)> { /// permutation((any, 'a')).parse_peek(input) /// } /// /// // any parses 'b', then char('a') parses 'a' /// assert_eq!(parser("ba"), Ok(("", ('b', 'a')))); /// /// // any parses 'a', then char('a') fails on 'b', /// // even though char('a') followed by any would succeed /// assert_eq!(parser("ab"), Err(ErrMode::Backtrack(InputError::new("b", ErrorKind::Verify)))); /// ``` /// pub fn permutation, 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.clone()); match branch.parse_next(input) { Err(ErrMode::Backtrack(e)) => { error = match error { Some(error) => Some(error.or(e)), None => Some(e), }; } res => return res, } } match error { Some(e) => Err(ErrMode::Backtrack(e.append(input, ErrorKind::Alt))), None => Err(ErrMode::assert(input, "`alt` needs at least one parser")), } } } macro_rules! alt_trait( ($first:ident $second:ident $($id: ident)+) => ( alt_trait!(__impl $first $second; $($id)+); ); (__impl $($current:ident)*; $head:ident $($id: ident)+) => ( alt_trait_impl!($($current)*); alt_trait!(__impl $($current)* $head; $($id)+); ); (__impl $($current:ident)*; $head:ident) => ( alt_trait_impl!($($current)*); alt_trait_impl!($($current)* $head); ); ); macro_rules! alt_trait_impl( ($($id:ident)+) => ( impl< I: Stream, Output, Error: ParserError, $($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! alt_trait_inner( ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({ $input.reset($start.clone()); match $self.$it.parse_next($input) { Err(ErrMode::Backtrack(e)) => { let err = $err.or(e); succ!($it, alt_trait_inner!($self, $input, $start, err, $($id)+)) } res => res, } }); ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({ Err(ErrMode::Backtrack($err.append($input, ErrorKind::Alt))) }); ); alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10 Alt11 Alt12 Alt13 Alt14 Alt15 Alt16 Alt17 Alt18 Alt19 Alt20 Alt21 Alt22); // Manually implement Alt for (A,), the 1-tuple type impl, 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.clone()); return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Alt))); } // All parsers were applied match res { ($(Some($item)),+) => return Ok(($($item),+)), _ => unreachable!(), } } } } ); ); macro_rules! permutation_trait_inner( ($it:tt, $self:expr, $input:ident, $start:ident, $res:expr, $err:expr, $head:ident $($id:ident)*) => ( if $res.$it.is_none() { $input.reset($start.clone()); match $self.$it.parse_next($input) { Ok(o) => { $res.$it = Some(o); continue; } Err(ErrMode::Backtrack(e)) => { $err = Some(match $err { Some(err) => err.or(e), None => e, }); } Err(e) => return Err(e), }; } succ!($it, permutation_trait_inner!($self, $input, $start, $res, $err, $($id)*)); ); ($it:tt, $self:expr, $input:ident, $start:ident, $res:expr, $err:expr,) => (); ); permutation_trait!( P1 O1 o1 P2 O2 o2 P3 O3 o3 P4 O4 o4 P5 O5 o5 P6 O6 o6 P7 O7 o7 P8 O8 o8 P9 O9 o9 P10 O10 o10 P11 O11 o11 P12 O12 o12 P13 O13 o13 P14 O14 o14 P15 O15 o15 P16 O16 o16 P17 O17 o17 P18 O18 o18 P19 O19 o19 P20 O20 o20 P21 O21 o21 ); winnow-0.5.15/src/combinator/core.rs000064400000000000000000000330061046102023000154610ustar 00000000000000use crate::error::{ErrMode, ErrorKind, Needed, ParserError}; use crate::stream::Stream; use crate::trace::trace; use crate::*; /// Return the remaining input. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::error::ErrorKind; /// # use winnow::error::InputError; /// use winnow::combinator::rest; /// assert_eq!(rest::<_,InputError<_>>.parse_peek("abc"), Ok(("", "abc"))); /// assert_eq!(rest::<_,InputError<_>>.parse_peek(""), Ok(("", ""))); /// ``` #[inline] pub fn rest>(input: &mut I) -> PResult<::Slice, E> where I: Stream, { trace("rest", move |input: &mut I| Ok(input.finish())).parse_next(input) } /// Return the length of the remaining input. /// /// Note: this does not advance the [`Stream`] /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::error::ErrorKind; /// # use winnow::error::InputError; /// use winnow::combinator::rest_len; /// assert_eq!(rest_len::<_,InputError<_>>.parse_peek("abc"), Ok(("abc", 3))); /// assert_eq!(rest_len::<_,InputError<_>>.parse_peek(""), Ok(("", 0))); /// ``` #[inline] pub fn rest_len>(input: &mut I) -> PResult where I: Stream, { trace("rest_len", move |input: &mut I| { let len = input.eof_offset(); Ok(len) }) .parse_next(input) } /// Apply a [`Parser`], producing `None` on [`ErrMode::Backtrack`]. /// /// To chain an error up, see [`cut_err`]. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::prelude::*; /// use winnow::combinator::opt; /// use winnow::ascii::alpha1; /// # fn main() { /// /// fn parser(i: &str) -> IResult<&str, Option<&str>> { /// opt(alpha1).parse_peek(i) /// } /// /// assert_eq!(parser("abcd;"), Ok((";", Some("abcd")))); /// assert_eq!(parser("123;"), Ok(("123;", None))); /// # } /// ``` pub fn opt, F>(mut f: F) -> impl Parser, E> where F: Parser, { trace("opt", move |input: &mut I| { let start = input.checkpoint(); match f.parse_next(input) { Ok(o) => Ok(Some(o)), Err(ErrMode::Backtrack(_)) => { input.reset(start); Ok(None) } Err(e) => Err(e), } }) } /// Calls the parser if the condition is met. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, IResult}; /// # use winnow::prelude::*; /// use winnow::combinator::cond; /// use winnow::ascii::alpha1; /// # fn main() { /// /// fn parser(b: bool, i: &str) -> IResult<&str, Option<&str>> { /// cond(b, alpha1).parse_peek(i) /// } /// /// assert_eq!(parser(true, "abcd;"), Ok((";", Some("abcd")))); /// assert_eq!(parser(false, "abcd;"), Ok(("abcd;", None))); /// assert_eq!(parser(true, "123;"), Err(ErrMode::Backtrack(InputError::new("123;", ErrorKind::Slice)))); /// assert_eq!(parser(false, "123;"), Ok(("123;", None))); /// # } /// ``` pub fn cond, F>(b: bool, mut f: F) -> impl Parser, E> where I: Stream, F: Parser, { trace("cond", move |input: &mut I| { if b { f.parse_next(input).map(Some) } else { Ok(None) } }) } /// Tries to apply its parser without consuming the input. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult}; /// # use winnow::prelude::*; /// use winnow::combinator::peek; /// use winnow::ascii::alpha1; /// # fn main() { /// /// let mut parser = peek(alpha1); /// /// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", "abcd"))); /// assert_eq!(parser.parse_peek("123;"), Err(ErrMode::Backtrack(InputError::new("123;", ErrorKind::Slice)))); /// # } /// ``` #[doc(alias = "look_ahead")] #[doc(alias = "rewind")] pub fn peek, F>(mut f: F) -> impl Parser where F: Parser, { trace("peek", move |input: &mut I| { let start = input.checkpoint(); let res = f.parse_next(input); input.reset(start); res }) } /// Match the end of the [`Stream`] /// /// Otherwise, it will error. /// /// # Example /// /// ```rust /// # use std::str; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::combinator::eof; /// # use winnow::prelude::*; /// /// let mut parser = eof; /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Eof)))); /// assert_eq!(parser.parse_peek(""), Ok(("", ""))); /// ``` #[doc(alias = "end")] #[doc(alias = "eoi")] pub fn eof>(input: &mut I) -> PResult<::Slice, E> where I: Stream, { trace("eof", move |input: &mut I| { if input.eof_offset() == 0 { Ok(input.next_slice(0)) } else { Err(ErrMode::from_error_kind(input, ErrorKind::Eof)) } }) .parse_next(input) } /// Succeeds if the child parser returns an error. /// /// **Note:** This does not advance the [`Stream`] /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult}; /// # use winnow::prelude::*; /// use winnow::combinator::not; /// use winnow::ascii::alpha1; /// # fn main() { /// /// let mut parser = not(alpha1); /// /// assert_eq!(parser.parse_peek("123"), Ok(("123", ()))); /// assert_eq!(parser.parse_peek("abcd"), Err(ErrMode::Backtrack(InputError::new("abcd", ErrorKind::Not)))); /// # } /// ``` pub fn not, F>(mut parser: F) -> impl Parser where F: Parser, { trace("not", move |input: &mut I| { let start = input.checkpoint(); let res = parser.parse_next(input); input.reset(start); match res { Ok(_) => Err(ErrMode::from_error_kind(input, ErrorKind::Not)), Err(ErrMode::Backtrack(_)) => Ok(()), Err(e) => Err(e), } }) } /// Transforms an [`ErrMode::Backtrack`] (recoverable) to [`ErrMode::Cut`] (unrecoverable) /// /// This commits the parse result, preventing alternative branch paths like with /// [`winnow::combinator::alt`][crate::combinator::alt]. /// /// # Example /// /// Without `cut_err`: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::token::one_of; /// # use winnow::ascii::digit1; /// # use winnow::combinator::rest; /// # use winnow::combinator::alt; /// # use winnow::combinator::preceded; /// # use winnow::prelude::*; /// # fn main() { /// /// fn parser(input: &str) -> IResult<&str, &str> { /// alt(( /// preceded(one_of(['+', '-']), digit1), /// rest /// )).parse_peek(input) /// } /// /// assert_eq!(parser("+10 ab"), Ok((" ab", "10"))); /// assert_eq!(parser("ab"), Ok(("", "ab"))); /// assert_eq!(parser("+"), Ok(("", "+"))); /// # } /// ``` /// /// With `cut_err`: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::prelude::*; /// # use winnow::token::one_of; /// # use winnow::ascii::digit1; /// # use winnow::combinator::rest; /// # use winnow::combinator::alt; /// # use winnow::combinator::preceded; /// use winnow::combinator::cut_err; /// # fn main() { /// /// fn parser(input: &str) -> IResult<&str, &str> { /// alt(( /// preceded(one_of(['+', '-']), cut_err(digit1)), /// rest /// )).parse_peek(input) /// } /// /// assert_eq!(parser("+10 ab"), Ok((" ab", "10"))); /// assert_eq!(parser("ab"), Ok(("", "ab"))); /// assert_eq!(parser("+"), Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice )))); /// # } /// ``` pub fn cut_err, F>(mut parser: F) -> impl Parser where I: Stream, F: Parser, { trace("cut_err", move |input: &mut I| { parser.parse_next(input).map_err(|e| e.cut()) }) } /// Transforms an [`ErrMode::Cut`] (unrecoverable) to [`ErrMode::Backtrack`] (recoverable) /// /// This attempts the parse, allowing other parsers to be tried on failure, like with /// [`winnow::combinator::alt`][crate::combinator::alt]. pub fn backtrack_err, F>(mut parser: F) -> impl Parser where I: Stream, F: Parser, { trace("backtrack_err", move |input: &mut I| { parser.parse_next(input).map_err(|e| e.backtrack()) }) } /// A placeholder for a not-yet-implemented [`Parser`] /// /// This is analogous to the [`todo!`] macro and helps with prototyping. /// /// # Panic /// /// This will panic when parsing /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::combinator::todo; /// /// fn parser(input: &mut &str) -> PResult { /// todo(input) /// } /// ``` #[track_caller] pub fn todo(input: &mut I) -> PResult where I: Stream, { #![allow(clippy::todo)] trace("todo", move |_input: &mut I| todo!("unimplemented parse")).parse_next(input) } /// Repeats the embedded parser, lazily returning the results /// /// Call the iterator's [`ParserIterator::finish`] method to get the remaining input if successful, /// or the error value if we encountered an error. /// /// On [`ErrMode::Backtrack`], iteration will stop. To instead chain an error up, see [`cut_err`]. /// /// # Example /// /// ```rust /// use winnow::{combinator::iterator, IResult, token::tag, ascii::alpha1, combinator::terminated}; /// use std::collections::HashMap; /// /// let data = "abc|defg|hijkl|mnopqr|123"; /// let mut it = iterator(data, terminated(alpha1, "|")); /// /// let parsed = it.map(|v| (v, v.len())).collect::>(); /// 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: I, parser: F) -> ParserIterator where F: Parser, I: Stream, E: 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<'a, F, I, O, E> core::iter::Iterator for &'a 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), } /// Always succeeds with given value without consuming any input. /// /// For example, it can be used as the last alternative in `alt` to /// specify the default case. /// /// **Note:** This never advances the [`Stream`] /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::prelude::*; /// use winnow::combinator::alt; /// use winnow::combinator::success; /// /// let mut parser = success::<_,_,InputError<_>>(10); /// assert_eq!(parser.parse_peek("xyz"), Ok(("xyz", 10))); /// /// fn sign(input: &str) -> IResult<&str, isize> { /// alt(( /// '-'.value(-1), /// '+'.value(1), /// success::<_,_,InputError<_>>(1) /// )).parse_peek(input) /// } /// assert_eq!(sign("+10"), Ok(("10", 1))); /// assert_eq!(sign("-10"), Ok(("10", -1))); /// assert_eq!(sign("10"), Ok(("10", 1))); /// ``` #[doc(alias = "value")] #[doc(alias = "empty")] pub fn success>(val: O) -> impl Parser { trace("success", move |_input: &mut I| Ok(val.clone())) } /// A parser which always fails. /// /// For example, it can be used as the last alternative in `alt` to /// control the error message given. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult}; /// # use winnow::prelude::*; /// use winnow::combinator::fail; /// /// let s = "string"; /// assert_eq!(fail::<_, &str, _>.parse_peek(s), Err(ErrMode::Backtrack(InputError::new(s, ErrorKind::Fail)))); /// ``` #[doc(alias = "unexpected")] pub fn fail>(i: &mut I) -> PResult { trace("fail", |i: &mut I| { Err(ErrMode::from_error_kind(i, ErrorKind::Fail)) }) .parse_next(i) } winnow-0.5.15/src/combinator/mod.rs000064400000000000000000000341001046102023000153040ustar 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 recognize the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer". //! //! | combinator | usage | input | output | comment | //! |---|---|---|---|---| //! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` | `"abc"` | `Ok(("bc", 'a'))` |Matches one of the provided characters (works with non ASCII characters too)| //! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` | `"xyab"` | `Ok(("yab", 'x'))` |Matches anything but the provided characters| //! | [`tag`][crate::token::tag] | `"hello"` | `"hello world"` | `Ok((" world", "hello"))` |Recognizes a specific suite of characters or bytes| //! | [`tag_no_case`][crate::token::tag_no_case] | `tag_no_case("hello")` | `"HeLLo World"` | `Ok((" World", "HeLLo"))` |Case insensitive comparison. Note that case insensitive comparison is not well defined for unicode, and that you might have bad surprises| //! | [`take`][crate::token::take] | `take(4)` | `"hello"` | `Ok(("o", "hell"))` |Takes a specific number of bytes or characters| //! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` | `"abc123"` | `Ok(("123", "abc"))` |Returns the longest list of bytes for which the provided pattern matches.| //! | [`take_till0`][crate::token::take_till0] | `take_till0(is_alphabetic)` | `"123abc"` | `Ok(("abc", "123"))` |Returns the longest list of bytes or characters until the provided pattern matches. `take_till1` does the same, but must return at least one character. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`| //! | [`take_until0`][crate::token::take_until0] | `take_until0("world")` | `"Hello world"` | `Ok(("world", "Hello "))` |Returns the longest list of bytes or characters until the provided tag is found. `take_until1` does the same, but must return at least one character| //! //! ## Choice combinators //! //! | combinator | usage | input | output | comment | //! |---|---|---|---|---| //! | [`alt`][crate::combinator::alt] | `alt(("ab", "cd"))` | `"cdef"` | `Ok(("ef", "cd"))` |Try a list of parsers and return the result of the first successful one| //! | [`dispatch`][crate::combinator::dispatch] | \- | \- | \- | `match` for parsers | //! | [`permutation`][crate::combinator::permutation] | `permutation(("ab", "cd", "12"))` | `"cd12abc"` | `Ok(("c", ("ab", "cd", "12"))` |Succeeds when all its child parser have succeeded, whatever the order| //! //! ## Sequence combinators //! //! | combinator | usage | input | output | comment | //! |---|---|---|---|---| //! | [`(...)` (tuples)][crate::Parser] | `("ab", "XY", take(1))` | `"abXYZ!"` | `Ok(("!", ("ab", "XY", "Z")))` |Chains parsers and assemble the sub results in a tuple. You can use as many child parsers as you can put elements in a tuple| //! | [`delimited`] | `delimited(char('('), take(2), char(')'))` | `"(ab)cd"` | `Ok(("cd", "ab"))` || //! | [`preceded`] | `preceded("ab", "XY")` | `"abXYZ"` | `Ok(("Z", "XY"))` || //! | [`terminated`] | `terminated("ab", "XY")` | `"abXYZ"` | `Ok(("Z", "ab"))` || //! | [`separated_pair`] | `separated_pair("hello", char(','), "world")` | `"hello,world!"` | `Ok(("!", ("hello", "world")))` || //! //! ## Applying a parser multiple times //! //! | combinator | usage | input | output | comment | //! |---|---|---|---|---| //! | [`repeat`][crate::combinator::repeat] | `repeat(1..=3, "ab")` | `"ababc"` | `Ok(("c", vec!["ab", "ab"]))` |Applies the parser between m and n times (n included) and returns the list of results in a Vec| //! | [`repeat_till0`][crate::combinator::repeat_till0] | `repeat_till0(tag( "ab" ), tag( "ef" ))` | `"ababefg"` | `Ok(("g", (vec!["ab", "ab"], "ef")))` |Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second| //! | [`separated0`][crate::combinator::separated0] | `separated0("ab", ",")` | `"ab,ab,ab."` | `Ok((".", vec!["ab", "ab", "ab"]))` |`separated1` works like `separated0` but must returns at least one element| //! | [`fold_repeat`][crate::combinator::fold_repeat] | `fold_repeat(1..=2, be_u8, \|\| 0, \|acc, item\| acc + item)` | `[1, 2, 3]` | `Ok(([3], 3))` |Applies the parser between m and n times (n included) and folds the list of return value| //! //! ## Partial related //! //! - [`eof`][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`][cond]: Conditional combinator. Wraps another parser and calls it if the condition is met //! - [`Parser::flat_map`][crate::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`][crate::Parser::value]: method to replace the result of a parser //! - [`Parser::map`][crate::Parser::map]: method to map a function on the result of a parser //! - [`Parser::and_then`][crate::Parser::and_then]: Applies a second parser over the output of the first one //! - [`Parser::verify_map`][Parser::verify_map]: Maps a function returning an `Option` on the output of a parser //! - [`Parser::try_map`][Parser::try_map]: Maps a function returning a `Result` on the output of a parser //! - [`Parser::parse_to`][crate::Parser::parse_to]: Apply [`std::str::FromStr`] to the output of the parser //! - [`not`][not]: Returns a result only if the embedded parser returns `Backtrack` or `Incomplete`. Does not consume the input //! - [`opt`][opt]: Make the underlying parser optional //! - [`peek`][peek]: Returns a result without consuming the input //! - [`Parser::recognize`][Parser::recognize]: If the child parser was successful, return the consumed input as the produced value //! - [`Parser::with_recognized`][Parser::with_recognized]: If the child parser was successful, return a tuple of the consumed input and the produced output. //! - [`Parser::span`][Parser::span]: If the child parser was successful, return the location of the consumed input as the produced value //! - [`Parser::with_span`][Parser::with_span]: If the child parser was successful, return a tuple of the location of the consumed input and the produced output. //! - [`Parser::verify`]: Returns the result of the child parser if it satisfies a verification function //! //! ## Error management and debugging //! //! - [`cut_err`]: Commit the parse result, disallowing alternative parsers from being attempted //! - [`backtrack_err`]: Attemmpts a parse, allowing alternative parsers to be attempted despite //! use of `cut_err` //! - [`Parser::context`]: Add context to the error if the parser fails //! - [`trace`][crate::trace::trace]: Print the parse state with the `debug` feature flag //! - [`todo()`]: Placeholder parser //! //! ## Remaining combinators //! //! - [`success`][success]: Returns a value without consuming any input, always succeeds //! - [`fail`][fail]: Inversion of `success`. Always fails. //! - [`Parser::by_ref`]: Allow moving `&mut impl Parser` into other parsers //! //! ## Text parsing //! //! - [`any`][crate::token::any]: Matches one token //! - [`tab`][crate::ascii::tab]: Matches a tab character `\t` //! - [`crlf`][crate::ascii::crlf]: Recognizes the string `\r\n` //! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`) //! - [`newline`][crate::ascii::newline]: Matches a newline character `\n` //! - [`not_line_ending`][crate::ascii::not_line_ending]: Recognizes a string of any char except `\r` or `\n` //! - [`rest`][rest]: Return the remaining input //! //! - [`alpha0`][crate::ascii::alpha0]: Recognizes zero or more lowercase and uppercase alphabetic characters: `[a-zA-Z]`. [`alpha1`][crate::ascii::alpha1] does the same but returns at least one character //! - [`alphanumeric0`][crate::ascii::alphanumeric0]: Recognizes zero or more numerical and alphabetic characters: `[0-9a-zA-Z]`. [`alphanumeric1`][crate::ascii::alphanumeric1] does the same but returns at least one character //! - [`space0`][crate::ascii::space0]: Recognizes zero or more spaces and tabs. [`space1`][crate::ascii::space1] does the same but returns at least one character //! - [`multispace0`][crate::ascii::multispace0]: Recognizes zero or more spaces, tabs, carriage returns and line feeds. [`multispace1`][crate::ascii::multispace1] does the same but returns at least one character //! - [`digit0`][crate::ascii::digit0]: Recognizes zero or more numerical characters: `[0-9]`. [`digit1`][crate::ascii::digit1] does the same but returns at least one character //! - [`hex_digit0`][crate::ascii::hex_digit0]: Recognizes zero or more hexadecimal numerical characters: `[0-9A-Fa-f]`. [`hex_digit1`][crate::ascii::hex_digit1] does the same but returns at least one character //! - [`oct_digit0`][crate::ascii::oct_digit0]: Recognizes zero or more octal characters: `[0-7]`. [`oct_digit1`][crate::ascii::oct_digit1] does the same but returns at least one character //! //! - [`float`][crate::ascii::float]: Parse a floating point number in a byte string //! - [`dec_int`][crate::ascii::dec_uint]: Decode a variable-width, decimal signed integer //! - [`dec_uint`][crate::ascii::dec_uint]: Decode a variable-width, decimal unsigned integer //! - [`hex_uint`][crate::ascii::hex_uint]: Decode a variable-width, hexadecimal integer //! //! - [`escaped`][crate::ascii::escaped]: Matches a byte string with escaped characters //! - [`escaped_transform`][crate::ascii::escaped_transform]: Matches a byte string with escaped characters, and returns a new string with the escaped characters replaced //! //! ### Character test functions //! //! Use these functions with a combinator like `take_while`: //! //! - [`AsChar::is_alpha`][crate::stream::AsChar::is_alpha]: Tests if byte is ASCII alphabetic: `[A-Za-z]` //! - [`AsChar::is_alphanum`][crate::stream::AsChar::is_alphanum]: Tests if byte is ASCII alphanumeric: `[A-Za-z0-9]` //! - [`AsChar::is_dec_digit`][crate::stream::AsChar::is_dec_digit]: Tests if byte is ASCII digit: `[0-9]` //! - [`AsChar::is_hex_digit`][crate::stream::AsChar::is_hex_digit]: Tests if byte is ASCII hex digit: `[0-9A-Fa-f]` //! - [`AsChar::is_oct_digit`][crate::stream::AsChar::is_oct_digit]: Tests if byte is ASCII octal digit: `[0-7]` //! - [`AsChar::is_space`][crate::stream::AsChar::is_space]: Tests if byte is ASCII space or tab: `[ \t]` //! - [`AsChar::is_newline`][crate::stream::AsChar::is_newline]: Tests if byte is ASCII newline: `[\n]` //! //! ## Binary format parsing //! //! - [`length_count`][crate::binary::length_count] Gets a number from the first parser, then applies the second parser that many times //! - [`length_data`][crate::binary::length_data]: Gets a number from the first parser, then takes a subslice of the input of that size, and returns that subslice //! - [`length_value`][crate::binary::length_value]: Gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns `Incomplete`, `length_value` will return an error //! //! ### Integers //! //! Parsing integers from binary formats can be done in two ways: With parser functions, or combinators with configurable endianness. //! //! - **configurable endianness:** [`i16`][crate::binary::i16], [`i32`][crate::binary::i32], //! [`i64`][crate::binary::i64], [`u16`][crate::binary::u16], [`u32`][crate::binary::u32], //! [`u64`][crate::binary::u64] are combinators that take as argument a //! [`winnow::binary::Endianness`][crate::binary::Endianness], like this: `i16(endianness)`. If the //! parameter is `winnow::binary::Endianness::Big`, parse a big endian `i16` integer, otherwise a //! little endian `i16` integer. //! - **fixed endianness**: The functions are prefixed by `be_` for big endian numbers, and by `le_` for little endian numbers, and the suffix is the type they parse to. As an example, `be_u32` parses a big endian unsigned integer stored in 32 bits. //! - [`be_f32`][crate::binary::be_f32], [`be_f64`][crate::binary::be_f64]: Big endian floating point numbers //! - [`le_f32`][crate::binary::le_f32], [`le_f64`][crate::binary::le_f64]: Little endian floating point numbers //! - [`be_i8`][crate::binary::be_i8], [`be_i16`][crate::binary::be_i16], [`be_i24`][crate::binary::be_i24], [`be_i32`][crate::binary::be_i32], [`be_i64`][crate::binary::be_i64], [`be_i128`][crate::binary::be_i128]: Big endian signed integers //! - [`be_u8`][crate::binary::be_u8], [`be_u16`][crate::binary::be_u16], [`be_u24`][crate::binary::be_u24], [`be_u32`][crate::binary::be_u32], [`be_u64`][crate::binary::be_u64], [`be_u128`][crate::binary::be_u128]: Big endian unsigned integers //! - [`le_i8`][crate::binary::le_i8], [`le_i16`][crate::binary::le_i16], [`le_i24`][crate::binary::le_i24], [`le_i32`][crate::binary::le_i32], [`le_i64`][crate::binary::le_i64], [`le_i128`][crate::binary::le_i128]: Little endian signed integers //! - [`le_u8`][crate::binary::le_u8], [`le_u16`][crate::binary::le_u16], [`le_u24`][crate::binary::le_u24], [`le_u32`][crate::binary::le_u32], [`le_u64`][crate::binary::le_u64], [`le_u128`][crate::binary::le_u128]: Little endian unsigned integers //! //! ### Bit stream parsing //! //! - [`bits`][crate::binary::bits::bits]: Transforms the current input type (byte slice `&[u8]`) to a bit stream on which bit specific parsers and more general combinators can be applied //! - [`bytes`][crate::binary::bits::bytes]: Transforms its bits stream input back into a byte slice for the underlying parser //! - [`take`][crate::binary::bits::take]: Take a set number of its //! - [`tag`][crate::binary::bits::tag]: Check if a set number of bis matches a pattern //! - [`bool`][crate::binary::bits::bool]: Match any one bit mod branch; mod core; mod multi; mod parser; mod sequence; #[cfg(test)] mod tests; pub use self::branch::*; pub use self::core::*; pub use self::multi::*; pub use self::parser::*; pub use self::sequence::*; #[allow(unused_imports)] use crate::Parser; winnow-0.5.15/src/combinator/multi.rs000064400000000000000000000710471046102023000156720ustar 00000000000000//! Combinators applying their child parser multiple times use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::ParserError; use crate::stream::Accumulate; use crate::stream::Range; use crate::stream::Stream; use crate::trace::trace; use crate::PResult; use crate::Parser; /// [`Accumulate`] the output of a parser into a container, like `Vec` /// /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Arguments /// * `m` The minimum number of iterations. /// * `n` The maximum number of iterations. /// * `f` The parser to apply. /// /// To recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`]. /// /// **Warning:** If the parser passed to `repeat` accepts empty inputs /// (like `alpha0` or `digit0`), `repeat` will return an error, /// to prevent going into an infinite loop. /// /// # Example /// /// Zero or more reptitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat(0.., "abc").parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); /// assert_eq!(parser("123123"), Ok(("123123", vec![]))); /// assert_eq!(parser(""), Ok(("", vec![]))); /// # } /// ``` /// /// One or more reptitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat(1.., "abc").parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); /// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// # } /// ``` /// /// Fixed number of repeitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat(2, "abc").parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); /// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); /// # } /// ``` /// /// Arbitrary reptitions: /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// repeat(0..=2, "abc").parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); /// assert_eq!(parser("123123"), Ok(("123123", vec![]))); /// assert_eq!(parser(""), Ok(("", vec![]))); /// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); /// # } /// ``` #[doc(alias = "many0")] #[doc(alias = "count")] #[doc(alias = "many0_count")] #[doc(alias = "many1")] #[doc(alias = "many1_count")] #[doc(alias = "many_m_n")] #[doc(alias = "repeated")] #[doc(alias = "skip_many")] #[doc(alias = "skip_many1")] #[inline(always)] pub fn repeat(range: impl Into, mut f: F) -> impl Parser where I: Stream, C: Accumulate, F: Parser, E: ParserError, { let Range { start_inclusive, end_inclusive, } = range.into(); trace("repeat", move |i: &mut I| { match (start_inclusive, end_inclusive) { (0, None) => repeat0_(&mut f, i), (1, None) => repeat1_(&mut f, i), (start, end) if Some(start) == end => repeat_n_(start, &mut f, i), (start, end) => repeat_m_n_(start, end.unwrap_or(usize::MAX), &mut f, i), } }) } fn repeat0_(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, { match f.parse_next(i) { Err(e) => Err(e.append(i, ErrorKind::Many)), Ok(o) => { let mut acc = C::initial(None); acc.accumulate(o); loop { let start = i.checkpoint(); let len = i.eof_offset(); match f.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(acc); } Err(e) => return Err(e), Ok(o) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert(i, "`repeat` parsers must always consume")); } acc.accumulate(o); } } } } } } /// [`Accumulate`] the output of parser `f` into a container, like `Vec`, until the parser `g` /// produces a result. /// /// Returns a tuple of the results of `f` in a `Vec` and the result of `g`. /// /// `f` keeps going so long as `g` produces [`ErrMode::Backtrack`]. To instead chain an error up, see [`cut_err`][crate::combinator::cut_err]. /// /// To recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`]. /// /// # Example /// /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::repeat_till0; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, (Vec<&str>, &str)> { /// repeat_till0("abc", "end").parse_peek(s) /// }; /// /// assert_eq!(parser("abcabcend"), Ok(("", (vec!["abc", "abc"], "end")))); /// assert_eq!(parser("abc123end"), Err(ErrMode::Backtrack(InputError::new("123end", ErrorKind::Tag)))); /// assert_eq!(parser("123123end"), Err(ErrMode::Backtrack(InputError::new("123123end", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser("abcendefg"), Ok(("efg", (vec!["abc"], "end")))); /// # } /// ``` #[doc(alias = "many_till0")] pub fn repeat_till0(mut f: F, mut g: G) -> impl Parser where I: Stream, C: Accumulate, F: Parser, G: Parser, E: ParserError, { trace("repeat_till0", move |i: &mut I| { let mut res = C::initial(None); loop { let start = i.checkpoint(); let len = i.eof_offset(); match g.parse_next(i) { Ok(o) => return Ok((res, o)), Err(ErrMode::Backtrack(_)) => { i.reset(start); match f.parse_next(i) { Err(e) => return Err(e.append(i, ErrorKind::Many)), Ok(o) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert( i, "`repeat` parsers must always consume", )); } res.accumulate(o); } } } Err(e) => return Err(e), } } }) } /// [`Accumulate`] the output of a parser, interleaed with `sep` /// /// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Arguments /// * `parser` Parses the elements of the list. /// * `sep` Parses the separator between list elements. /// /// # Example /// /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated0; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// separated0("abc", "|").parse_peek(s) /// } /// /// assert_eq!(parser("abc|abc|abc"), Ok(("", vec!["abc", "abc", "abc"]))); /// assert_eq!(parser("abc123abc"), Ok(("123abc", vec!["abc"]))); /// assert_eq!(parser("abc|def"), Ok(("|def", vec!["abc"]))); /// assert_eq!(parser(""), Ok(("", vec![]))); /// assert_eq!(parser("def|abc"), Ok(("def|abc", vec![]))); /// # } /// ``` #[doc(alias = "sep_by")] #[doc(alias = "separated_list0")] pub fn separated0(mut parser: P, mut sep: S) -> impl Parser where I: Stream, C: Accumulate, P: Parser, S: Parser, E: ParserError, { trace("separated0", move |i: &mut I| { let mut res = C::initial(None); let start = i.checkpoint(); match parser.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(res); } Err(e) => return Err(e), Ok(o) => { res.accumulate(o); } } loop { let start = i.checkpoint(); let len = i.eof_offset(); match sep.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(res); } Err(e) => return Err(e), Ok(_) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert(i, "sep parsers must always consume")); } match parser.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(res); } Err(e) => return Err(e), Ok(o) => { res.accumulate(o); } } } } } }) } /// [`Accumulate`] the output of a parser, interleaed with `sep` /// /// Fails if the element parser does not produce at least one element.$ /// /// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Arguments /// * `sep` Parses the separator between list elements. /// * `f` Parses the elements of the list. /// /// # Example /// /// ```rust /// # #[cfg(feature = "std")] { /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated1; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// separated1("abc", "|").parse_peek(s) /// } /// /// assert_eq!(parser("abc|abc|abc"), Ok(("", vec!["abc", "abc", "abc"]))); /// assert_eq!(parser("abc123abc"), Ok(("123abc", vec!["abc"]))); /// assert_eq!(parser("abc|def"), Ok(("|def", vec!["abc"]))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Tag)))); /// # } /// ``` #[doc(alias = "sep_by1")] #[doc(alias = "separated_list1")] pub fn separated1(mut parser: P, mut sep: S) -> impl Parser where I: Stream, C: Accumulate, P: Parser, S: Parser, E: ParserError, { trace("separated1", move |i: &mut I| { let mut res = C::initial(None); // Parse the first element match parser.parse_next(i) { Err(e) => return Err(e), Ok(o) => { res.accumulate(o); } } loop { let start = i.checkpoint(); let len = i.eof_offset(); match sep.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(res); } Err(e) => return Err(e), Ok(_) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert(i, "sep parsers must always consume")); } match parser.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(res); } Err(e) => return Err(e), Ok(o) => { res.accumulate(o); } } } } } }) } /// Alternates between two parsers, merging the results (left associative) /// /// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated_foldl1; /// use winnow::ascii::dec_int; /// /// fn parser(s: &str) -> IResult<&str, i32> { /// separated_foldl1(dec_int, "-", |l, _, r| l - r).parse_peek(s) /// } /// /// assert_eq!(parser("9-3-5"), Ok(("", 1))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Slice)))); /// ``` pub fn separated_foldl1( mut parser: P, mut sep: S, op: Op, ) -> impl Parser where I: Stream, P: Parser, S: Parser, E: ParserError, Op: Fn(O, O2, O) -> O, { trace("separated_foldl1", move |i: &mut I| { let mut ol = parser.parse_next(i)?; loop { let start = i.checkpoint(); let len = i.eof_offset(); match sep.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(ol); } Err(e) => return Err(e), Ok(s) => { // infinite loop check: the parser must always consume if i.eof_offset() == len { return Err(ErrMode::assert(i, "`repeat` parsers must always consume")); } match parser.parse_next(i) { Err(ErrMode::Backtrack(_)) => { i.reset(start); return Ok(ol); } Err(e) => return Err(e), Ok(or) => { ol = op(ol, s, or); } } } } } }) } /// Alternates between two parsers, merging the results (right associative) /// /// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Example /// /// ``` /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::separated_foldr1; /// use winnow::ascii::dec_uint; /// /// fn parser(s: &str) -> IResult<&str, u32> { /// separated_foldr1(dec_uint, "^", |l: u32, _, r: u32| l.pow(r)).parse_peek(s) /// } /// /// assert_eq!(parser("2^3^2"), Ok(("", 512))); /// assert_eq!(parser("2"), Ok(("", 2))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Slice)))); /// ``` #[cfg(feature = "alloc")] pub fn separated_foldr1( mut parser: P, mut sep: S, op: Op, ) -> impl Parser where I: Stream, P: Parser, S: Parser, E: ParserError, Op: Fn(O, O2, O) -> O, { trace("separated_foldr1", move |i: &mut I| { let ol = parser.parse_next(i)?; let all: crate::lib::std::vec::Vec<(O2, O)> = repeat(0.., (sep.by_ref(), parser.by_ref())).parse_next(i)?; if let Some((s, or)) = all .into_iter() .rev() .reduce(|(sr, or), (sl, ol)| (sl, op(ol, sr, or))) { let merged = op(ol, s, or); Ok(merged) } else { Ok(ol) } }) } fn repeat_m_n_(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::Cut(E::from_error_kind(input, ErrorKind::Many))); } let mut res = C::initial(Some(min)); for count in 0..max { let start = input.checkpoint(); let len = input.eof_offset(); match parse.parse_next(input) { Ok(value) => { // infinite loop check: the parser must always consume if input.eof_offset() == len { return Err(ErrMode::assert( input, "`repeat` parsers must always consume", )); } res.accumulate(value); } Err(ErrMode::Backtrack(e)) => { if count < min { return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many))); } else { input.reset(start); return Ok(res); } } Err(e) => { return Err(e); } } } Ok(res) } fn repeat_n_(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 { match f.parse_next(i) { Ok(o) => { res.accumulate(o); } Err(e) => { return Err(e.append(i, ErrorKind::Many)); } } } Ok(res) } /// Repeats the embedded parser, filling the given slice with results. /// /// This parser fails if the input runs out before the given slice is full. /// /// # Arguments /// * `f` The parser to apply. /// * `buf` The slice to fill /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::fill; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, [&str; 2]> { /// let mut buf = ["", ""]; /// let (rest, ()) = fill("abc", &mut buf).parse_peek(s)?; /// Ok((rest, buf)) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", ["abc", "abc"]))); /// assert_eq!(parser("abc123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); /// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser("abcabcabc"), Ok(("abc", ["abc", "abc"]))); /// ``` pub fn fill<'a, I, O, E, F>(mut f: F, buf: &'a mut [O]) -> impl Parser + 'a where I: Stream + 'a, F: Parser + 'a, E: ParserError + 'a, { trace("fill", move |i: &mut I| { for elem in buf.iter_mut() { match f.parse_next(i) { Ok(o) => { *elem = o; } Err(e) => { return Err(e.append(i, ErrorKind::Many)); } } } Ok(()) }) } /// Repeats the embedded parser `m..=n` times, calling `g` to gather the results /// /// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see /// [`cut_err`][crate::combinator::cut_err]. /// /// # Arguments /// * `m` The minimum number of iterations. /// * `n` The maximum number of iterations. /// * `f` The parser to apply. /// * `init` A function returning the initial value. /// * `g` The function that combines a result of `f` with /// the current accumulator. /// /// **Warning:** If the parser passed to `fold_repeat` accepts empty inputs /// (like `alpha0` or `digit0`), `fold_repeat` will return an error, /// to prevent going into an infinite loop. /// /// # Example /// /// Zero or more repetitions: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::fold_repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// fold_repeat( /// 0.., /// "abc", /// Vec::new, /// |mut acc: Vec<_>, item| { /// acc.push(item); /// acc /// } /// ).parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); /// assert_eq!(parser("123123"), Ok(("123123", vec![]))); /// assert_eq!(parser(""), Ok(("", vec![]))); /// ``` /// /// One or more repetitions: /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::fold_repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// fold_repeat( /// 1.., /// "abc", /// Vec::new, /// |mut acc: Vec<_>, item| { /// acc.push(item); /// acc /// } /// ).parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); /// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Many)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Many)))); /// ``` /// /// Arbitrary number of repetitions: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; /// # use winnow::prelude::*; /// use winnow::combinator::fold_repeat; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, Vec<&str>> { /// fold_repeat( /// 0..=2, /// "abc", /// Vec::new, /// |mut acc: Vec<_>, item| { /// acc.push(item); /// acc /// } /// ).parse_peek(s) /// } /// /// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); /// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); /// assert_eq!(parser("123123"), Ok(("123123", vec![]))); /// assert_eq!(parser(""), Ok(("", vec![]))); /// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); /// ``` #[doc(alias = "fold_many0")] #[doc(alias = "fold_many1")] #[doc(alias = "fold_many_m_n")] #[inline(always)] pub fn fold_repeat( range: impl Into, mut f: F, mut init: H, mut g: G, ) -> impl Parser where I: Stream, F: Parser, G: FnMut(R, O) -> R, H: FnMut() -> R, E: ParserError, { let Range { start_inclusive, end_inclusive, } = range.into(); trace("fold_repeat", move |i: &mut I| { match (start_inclusive, end_inclusive) { (0, None) => fold_repeat0_(&mut f, &mut init, &mut g, i), (1, None) => fold_repeat1_(&mut f, &mut init, &mut g, i), (start, end) => fold_repeat_m_n_( start, end.unwrap_or(usize::MAX), &mut f, &mut init, &mut g, i, ), } }) } fn fold_repeat0_( 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(); match f.parse_next(input) { Err(ErrMode::Backtrack(_)) => Err(ErrMode::from_error_kind(input, ErrorKind::Many)), Err(e) => Err(e), Ok(o1) => { let mut acc = g(init, o1); loop { let start = input.checkpoint(); let len = input.eof_offset(); match f.parse_next(input) { Err(ErrMode::Backtrack(_)) => { input.reset(start); break; } Err(e) => return Err(e), Ok(o) => { // infinite loop check: the parser must always consume if input.eof_offset() == len { return Err(ErrMode::assert( input, "`repeat` parsers must always consume", )); } acc = g(acc, o); } } } Ok(acc) } } } fn fold_repeat_m_n_( 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::Cut(E::from_error_kind(input, ErrorKind::Many))); } let mut acc = init(); for count in 0..max { let start = input.checkpoint(); let len = input.eof_offset(); match parse.parse_next(input) { Ok(value) => { // infinite loop check: the parser must always consume if input.eof_offset() == len { return Err(ErrMode::assert( input, "`repeat` parsers must always consume", )); } acc = fold(acc, value); } //FInputXMError: handle failure properly Err(ErrMode::Backtrack(err)) => { if count < min { return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Many))); } else { input.reset(start); break; } } Err(e) => return Err(e), } } Ok(acc) } winnow-0.5.15/src/combinator/parser.rs000064400000000000000000000510151046102023000160250ustar 00000000000000use crate::error::{AddContext, ErrMode, ErrorKind, FromExternalError, ParserError}; use crate::lib::std::borrow::Borrow; use crate::lib::std::ops::Range; use crate::stream::StreamIsPartial; use crate::stream::{Location, Stream}; use crate::trace::trace; use crate::trace::trace_result; use crate::*; /// Implementation of [`Parser::by_ref`][Parser::by_ref] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct ByRef<'p, P> { p: &'p mut P, } impl<'p, P> ByRef<'p, P> { #[inline(always)] pub(crate) fn new(p: &'p mut P) -> Self { Self { p } } } impl<'p, I, O, E, P> Parser for ByRef<'p, P> where P: Parser, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { self.p.parse_next(i) } } /// Implementation of [`Parser::map`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Map where F: Parser, G: Fn(O) -> O2, { parser: F, map: G, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl Map where F: Parser, G: Fn(O) -> O2, { #[inline(always)] pub(crate) fn new(parser: F, map: G) -> Self { Self { parser, map, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } impl Parser for Map where F: Parser, G: Fn(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)), } } } /// Implementation of [`Parser::try_map`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct TryMap where F: Parser, G: FnMut(O) -> Result, I: Stream, E: FromExternalError, { parser: F, map: G, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, e2: core::marker::PhantomData, } impl TryMap where F: Parser, G: FnMut(O) -> Result, I: Stream, E: FromExternalError, { #[inline(always)] pub(crate) fn new(parser: F, map: G) -> Self { Self { parser, map, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), e2: Default::default(), } } } impl 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 } } /// Implementation of [`Parser::verify_map`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct VerifyMap where F: Parser, G: FnMut(O) -> Option, I: Stream, E: ParserError, { parser: F, map: G, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl VerifyMap where F: Parser, G: FnMut(O) -> Option, I: Stream, E: ParserError, { #[inline(always)] pub(crate) fn new(parser: F, map: G) -> Self { Self { parser, map, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } impl 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 } } /// Implementation of [`Parser::and_then`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct AndThen where F: Parser, G: Parser, O: StreamIsPartial, I: Stream, { outer: F, inner: G, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl AndThen where F: Parser, G: Parser, O: StreamIsPartial, I: Stream, { #[inline(always)] pub(crate) fn new(outer: F, inner: G) -> Self { Self { outer, inner, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } impl 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) } } /// Implementation of [`Parser::parse_to`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct ParseTo where P: Parser, I: Stream, O: crate::stream::ParseSlice, E: ParserError, { p: P, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl ParseTo where P: Parser, I: Stream, O: crate::stream::ParseSlice, E: ParserError, { #[inline(always)] pub(crate) fn new(p: P) -> Self { Self { p, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } 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 } } /// Implementation of [`Parser::flat_map`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct FlatMap where F: Parser, G: FnMut(O) -> H, H: Parser, { f: F, g: G, h: core::marker::PhantomData, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl FlatMap where F: Parser, G: FnMut(O) -> H, H: Parser, { #[inline(always)] pub(crate) fn new(f: F, g: G) -> Self { Self { f, g, h: Default::default(), i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } impl 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) } } /// Implementation of [`Parser::complete_err`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct CompleteErr { f: F, } impl CompleteErr { #[inline(always)] pub(crate) fn new(f: F) -> Self { Self { 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) } } /// Implementation of [`Parser::verify`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Verify where F: Parser, G: Fn(&O2) -> bool, I: Stream, O: Borrow, O2: ?Sized, E: ParserError, { parser: F, filter: G, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl Verify where F: Parser, G: Fn(&O2) -> bool, I: Stream, O: Borrow, O2: ?Sized, E: ParserError, { #[inline(always)] pub(crate) fn new(parser: F, filter: G) -> Self { Self { parser, filter, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } impl Parser for Verify where F: Parser, G: Fn(&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 } } /// Implementation of [`Parser::value`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Value where F: Parser, O2: Clone, { parser: F, val: O2, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl Value where F: Parser, O2: Clone, { #[inline(always)] pub(crate) fn new(parser: F, val: O2) -> Self { Self { parser, val, i: Default::default(), o: Default::default(), e: Default::default(), } } } impl 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()) } } /// Implementation of [`Parser::void`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Void where F: Parser, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl Void where F: Parser, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), e: Default::default(), } } } 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(|_| ()) } } /// Implementation of [`Parser::recognize`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Recognize where F: Parser, I: Stream, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl Recognize where F: Parser, I: Stream, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), e: Default::default(), } } } impl Parser::Slice, E> for Recognize 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 recognized = input.next_slice(offset); Ok(recognized) } Err(e) => Err(e), } } } /// Implementation of [`Parser::with_recognized`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct WithRecognized where F: Parser, I: Stream, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl WithRecognized where F: Parser, I: Stream, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), e: Default::default(), } } } impl Parser::Slice), E> for WithRecognized 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 recognized = input.next_slice(offset); Ok((result, recognized)) } Err(e) => Err(e), } } } /// Implementation of [`Parser::span`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Span where F: Parser, I: Stream + Location, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl Span where F: Parser, I: Stream + Location, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), e: Default::default(), } } } 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 }) } } /// Implementation of [`Parser::with_span`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct WithSpan where F: Parser, I: Stream + Location, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl WithSpan where F: Parser, I: Stream + Location, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), e: Default::default(), } } } 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)) }) } } /// Implementation of [`Parser::output_into`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct OutputInto where F: Parser, O: Into, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, o2: core::marker::PhantomData, e: core::marker::PhantomData, } impl OutputInto where F: Parser, O: Into, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), o2: Default::default(), e: Default::default(), } } } 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()) } } /// Implementation of [`Parser::err_into`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct ErrInto where F: Parser, E: Into, { parser: F, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, e2: core::marker::PhantomData, } impl ErrInto where F: Parser, E: Into, { #[inline(always)] pub(crate) fn new(parser: F) -> Self { Self { parser, i: Default::default(), o: Default::default(), e: Default::default(), e2: Default::default(), } } } impl Parser for ErrInto where F: Parser, E: Into, { #[inline] fn parse_next(&mut self, i: &mut I) -> PResult { match self.parser.parse_next(i) { Ok(ok) => Ok(ok), Err(ErrMode::Backtrack(e)) => Err(ErrMode::Backtrack(e.into())), Err(ErrMode::Cut(e)) => Err(ErrMode::Cut(e.into())), Err(ErrMode::Incomplete(e)) => Err(ErrMode::Incomplete(e)), } } } /// Implementation of [`Parser::context`] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub struct Context where F: Parser, I: Stream, E: AddContext, C: Clone + crate::lib::std::fmt::Debug, { parser: F, context: C, i: core::marker::PhantomData, o: core::marker::PhantomData, e: core::marker::PhantomData, } impl Context where F: Parser, I: Stream, E: AddContext, C: Clone + crate::lib::std::fmt::Debug, { #[inline(always)] pub(crate) fn new(parser: F, context: C) -> Self { Self { parser, context, i: Default::default(), o: Default::default(), e: Default::default(), } } } impl 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 { #[cfg(feature = "debug")] let name = format!("context={:?}", self.context); #[cfg(not(feature = "debug"))] let name = "context"; trace(name, move |i: &mut I| { (self.parser) .parse_next(i) .map_err(|err| err.add_context(i, self.context.clone())) }) .parse_next(i) } } winnow-0.5.15/src/combinator/sequence.rs000064400000000000000000000121441046102023000163410ustar 00000000000000use crate::error::ParserError; use crate::stream::Stream; use crate::trace::trace; use crate::*; /// Sequence two parsers, only returning the output from the second. /// /// # Arguments /// * `first` The opening parser. /// * `second` The second parser to get object. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::combinator::preceded; /// use winnow::token::tag; /// /// let mut parser = preceded("abc", "efg"); /// /// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "efg"))); /// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "efg"))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); /// ``` #[doc(alias = "ignore_then")] pub fn preceded, F, G>( mut first: F, mut second: G, ) -> impl Parser where I: Stream, F: Parser, G: Parser, { trace("preceded", move |input: &mut I| { let _ = first.parse_next(input)?; second.parse_next(input) }) } /// Sequence two parsers, only returning the output of the first. /// /// # Arguments /// * `first` The first parser to apply. /// * `second` The second parser to match an object. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::error::Needed::Size; /// use winnow::combinator::terminated; /// use winnow::token::tag; /// /// let mut parser = terminated("abc", "efg"); /// /// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "abc"))); /// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "abc"))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); /// ``` #[doc(alias = "then_ignore")] pub fn terminated, F, G>( mut first: F, mut second: G, ) -> impl Parser where I: Stream, F: Parser, G: Parser, { trace("terminated", move |input: &mut I| { let o1 = first.parse_next(input)?; second.parse_next(input).map(|_| o1) }) } /// Sequence three parsers, only returning the values of the first and third. /// /// # Arguments /// * `first` The first parser to apply. /// * `sep` The separator parser to apply. /// * `second` The second parser to apply. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::error::Needed::Size; /// # use winnow::prelude::*; /// use winnow::combinator::separated_pair; /// use winnow::token::tag; /// /// let mut parser = separated_pair("abc", "|", "efg"); /// /// assert_eq!(parser.parse_peek("abc|efg"), Ok(("", ("abc", "efg")))); /// assert_eq!(parser.parse_peek("abc|efghij"), Ok(("hij", ("abc", "efg")))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); /// ``` pub fn separated_pair, F, G, H>( mut first: F, mut sep: G, mut second: H, ) -> impl Parser where I: Stream, F: Parser, G: Parser, H: Parser, { trace("separated_pair", move |input: &mut I| { let o1 = first.parse_next(input)?; let _ = sep.parse_next(input)?; second.parse_next(input).map(|o2| (o1, o2)) }) } /// Sequence three parsers, only returning the output of the second. /// /// # Arguments /// * `first` The first parser to apply and discard. /// * `second` The second parser to apply. /// * `third` The third parser to apply and discard. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::error::Needed::Size; /// # use winnow::prelude::*; /// use winnow::combinator::delimited; /// use winnow::token::tag; /// /// let mut parser = delimited("(", "abc", ")"); /// /// assert_eq!(parser.parse_peek("(abc)"), Ok(("", "abc"))); /// assert_eq!(parser.parse_peek("(abc)def"), Ok(("def", "abc"))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag)))); /// ``` #[doc(alias = "between")] #[doc(alias = "padded")] pub fn delimited, F, G, H>( mut first: F, mut second: G, mut third: H, ) -> impl Parser where I: Stream, F: Parser, G: Parser, H: Parser, { trace("delimited", move |input: &mut I| { let _ = first.parse_next(input)?; let o2 = second.parse_next(input)?; third.parse_next(input).map(|_| o2) }) } winnow-0.5.15/src/combinator/tests.rs000064400000000000000000001037471046102023000157050ustar 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; use crate::stream::Stream; use crate::token::take; use crate::unpeek; use crate::IResult; use crate::PResult; use crate::Parser; use crate::Partial; #[cfg(feature = "alloc")] use crate::lib::std::vec::Vec; macro_rules! assert_parse( ($left: expr, $right: expr) => { let res: $crate::IResult<_, _, InputError<_>> = $left; assert_eq!(res, $right); }; ); #[test] fn eof_on_slices() { let not_over: &[u8] = &b"Hello, world!"[..]; let is_over: &[u8] = &b""[..]; let res_not_over = eof.parse_peek(not_over); assert_parse!( res_not_over, Err(ErrMode::Backtrack(error_position!( ¬_over, ErrorKind::Eof ))) ); let res_over = eof.parse_peek(is_over); assert_parse!(res_over, Ok((is_over, is_over))); } #[test] fn eof_on_strs() { let not_over: &str = "Hello, world!"; let is_over: &str = ""; let res_not_over = eof.parse_peek(not_over); assert_parse!( res_not_over, Err(ErrMode::Backtrack(error_position!( ¬_over, ErrorKind::Eof ))) ); let res_over = eof.parse_peek(is_over); assert_parse!(res_over, Ok((is_over, is_over))); } #[test] fn rest_on_slices() { let input: &[u8] = &b"Hello, world!"[..]; let empty: &[u8] = &b""[..]; assert_parse!(rest.parse_peek(input), Ok((empty, input))); } #[test] fn rest_on_strs() { let input: &str = "Hello, world!"; let empty: &str = ""; assert_parse!(rest.parse_peek(input), Ok((empty, input))); } #[test] fn rest_len_on_slices() { let input: &[u8] = &b"Hello, world!"[..]; assert_parse!(rest_len.parse_peek(input), Ok((input, input.len()))); } use crate::lib::std::convert::From; impl From for CustomError { fn from(_: u32) -> Self { CustomError } } impl ParserError for CustomError { fn from_error_kind(_: &I, _: ErrorKind) -> Self { CustomError } fn append(self, _: &I, _: ErrorKind) -> Self { CustomError } } struct CustomError; #[allow(dead_code)] fn custom_error(input: &[u8]) -> IResult<&[u8], &[u8], CustomError> { //fix_error!(input, CustomError<_>, alphanumeric) crate::ascii::alphanumeric1.parse_peek(input) } #[test] fn test_parser_flat_map() { let input: &[u8] = &[3, 100, 101, 102, 103, 104][..]; assert_parse!( u8.flat_map(take).parse_peek(input), Ok((&[103, 104][..], &[100, 101, 102][..])) ); } #[allow(dead_code)] fn test_closure_compiles_195(input: &[u8]) -> IResult<&[u8], ()> { u8.flat_map(|num| repeat(num as usize, u16(Endianness::Big))) .parse_peek(input) } #[test] fn test_parser_verify_map() { let input: &[u8] = &[50][..]; assert_parse!( u8.verify_map(|u| if u < 20 { Some(u) } else { None }) .parse_peek(input), Err(ErrMode::Backtrack(InputError::new( &[50][..], ErrorKind::Verify ))) ); assert_parse!( u8.verify_map(|u| if u > 20 { Some(u) } else { None }) .parse_peek(input), Ok((&[][..], 50)) ); } #[test] fn test_parser_map_parser() { let input: &[u8] = &[100, 101, 102, 103, 104][..]; assert_parse!( take(4usize).and_then(take(2usize)).parse_peek(input), Ok((&[104][..], &[100, 101][..])) ); } #[test] #[cfg(feature = "std")] fn test_parser_into() { use crate::error::InputError; use crate::token::take; let mut parser = take::<_, _, InputError<_>>(3u8).output_into(); let result: IResult<&[u8], Vec> = 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_tag(i: Partial<&[u8]>) -> IResult, &[u8]> { peek("abcd").parse_peek(i) } assert_eq!( peek_tag(Partial::new(&b"abcdef"[..])), Ok((Partial::new(&b"abcdef"[..]), &b"abcd"[..])) ); assert_eq!( peek_tag(Partial::new(&b"ab"[..])), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( peek_tag(Partial::new(&b"xxx"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"xxx"[..]), ErrorKind::Tag ))) ); } #[test] fn not_test() { fn not_aaa(i: Partial<&[u8]>) -> IResult, ()> { 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, ToString}, }, }; #[cfg(feature = "alloc")] #[derive(Debug, Clone, Eq, PartialEq)] pub 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, kind: ErrorKind) -> Self { ErrorStr(format!( "custom error message: ({:?}, {:?}) - {:?}", input, kind, self )) } } fn work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { Ok(input.peek_finish()) } #[allow(unused_variables)] fn dont_work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { Err(ErrMode::Backtrack(ErrorStr("abcd".to_string()))) } fn work2(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { Ok((input, &b""[..])) } fn alt1(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { alt((unpeek(dont_work), unpeek(dont_work))).parse_peek(i) } fn alt2(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { alt((unpeek(dont_work), unpeek(work))).parse_peek(i) } fn alt3(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> { alt(( unpeek(dont_work), unpeek(dont_work), unpeek(work2), unpeek(dont_work), )) .parse_peek(i) } //named!(alt1, alt!(dont_work | dont_work)); //named!(alt2, alt!(dont_work | work)); //named!(alt3, alt!(dont_work | dont_work | work2 | dont_work)); let a = &b"abcd"[..]; assert_eq!( alt1(a), Err(ErrMode::Backtrack(error_node_position!( &a, ErrorKind::Alt, ErrorStr("abcd".to_string()) ))) ); assert_eq!(alt2(a), Ok((&b""[..], a))); assert_eq!(alt3(a), Ok((a, &b""[..]))); fn alt4(i: &[u8]) -> IResult<&[u8], &[u8]> { alt(("abcd", "efgh")).parse_peek(i) } let b = &b"efgh"[..]; assert_eq!(alt4(a), Ok((&b""[..], a))); assert_eq!(alt4(b), Ok((&b""[..], b))); } #[test] fn alt_incomplete() { fn alt1(i: Partial<&[u8]>) -> IResult, &[u8]> { alt(("a", "bc", "def")).parse_peek(i) } let a = &b""[..]; assert_eq!( alt1(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); let a = &b"b"[..]; assert_eq!( alt1(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); let a = &b"bcd"[..]; assert_eq!( alt1(Partial::new(a)), Ok((Partial::new(&b"d"[..]), &b"bc"[..])) ); let a = &b"cde"[..]; assert_eq!( alt1(Partial::new(a)), Err(ErrMode::Backtrack(error_position!( &Partial::new(a), ErrorKind::Tag ))) ); let a = &b"de"[..]; assert_eq!( alt1(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))) ); let a = &b"defg"[..]; assert_eq!( alt1(Partial::new(a)), Ok((Partial::new(&b"g"[..]), &b"def"[..])) ); } #[test] fn alt_array() { fn alt1<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8]> { alt(["a", "bc", "def"]).parse_next(i) } let i = &b"a"[..]; assert_eq!(alt1.parse_peek(i), Ok((&b""[..], (&b"a"[..])))); let i = &b"bc"[..]; assert_eq!(alt1.parse_peek(i), Ok((&b""[..], (&b"bc"[..])))); let i = &b"defg"[..]; assert_eq!(alt1.parse_peek(i), Ok((&b"g"[..], (&b"def"[..])))); let i = &b"z"[..]; assert_eq!( alt1.parse_peek(i), Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Tag))) ); } #[test] fn permutation_test() { #[allow(clippy::type_complexity)] fn perm(i: Partial<&[u8]>) -> IResult, (&[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]>> { separated0("abcd", ",").parse_peek(i) } fn multi_empty(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated0("", ",").parse_peek(i) } fn multi_longsep(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated0("abcd", "..").parse_peek(i) } let a = &b"abcdef"[..]; let b = &b"abcd,abcdef"[..]; let c = &b"azerty"[..]; let d = &b",,abc"[..]; let e = &b"abcd,abcd,ef"[..]; let f = &b"abc"[..]; let g = &b"abcd."[..]; let h = &b"abcd,abc"[..]; let res1 = vec![&b"abcd"[..]]; assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; assert_eq!(multi(Partial::new(b)), Ok((Partial::new(&b"ef"[..]), res2))); assert_eq!( multi(Partial::new(c)), Ok((Partial::new(&b"azerty"[..]), Vec::new())) ); let res3 = vec![&b""[..], &b""[..], &b""[..]]; assert_eq!( multi_empty(Partial::new(d)), Ok((Partial::new(&b"abc"[..]), res3)) ); let res4 = vec![&b"abcd"[..], &b"abcd"[..]]; assert_eq!( multi(Partial::new(e)), Ok((Partial::new(&b",ef"[..]), res4)) ); assert_eq!( multi(Partial::new(f)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( multi_longsep(Partial::new(g)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( multi(Partial::new(h)), Err(ErrMode::Incomplete(Needed::new(1))) ); } #[test] #[cfg(feature = "alloc")] #[cfg_attr(debug_assertions, should_panic)] fn separated0_empty_sep_test() { fn empty_sep(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated0("abc", "").parse_peek(i) } let i = &b"abcabc"[..]; let i_err_pos = &i[3..]; assert_eq!( empty_sep(Partial::new(i)), Err(ErrMode::Backtrack(error_position!( &Partial::new(i_err_pos), ErrorKind::Assert ))) ); } #[test] #[cfg(feature = "alloc")] fn separated1_test() { fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated1("abcd", ",").parse_peek(i) } fn multi_longsep(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { separated1("abcd", "..").parse_peek(i) } let a = &b"abcdef"[..]; let b = &b"abcd,abcdef"[..]; let c = &b"azerty"[..]; let d = &b"abcd,abcd,ef"[..]; let f = &b"abc"[..]; let g = &b"abcd."[..]; let h = &b"abcd,abc"[..]; let res1 = vec![&b"abcd"[..]]; assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; assert_eq!(multi(Partial::new(b)), Ok((Partial::new(&b"ef"[..]), res2))); assert_eq!( multi(Partial::new(c)), Err(ErrMode::Backtrack(error_position!( &Partial::new(c), ErrorKind::Tag ))) ); let res3 = vec![&b"abcd"[..], &b"abcd"[..]]; assert_eq!( multi(Partial::new(d)), Ok((Partial::new(&b",ef"[..]), res3)) ); assert_eq!( multi(Partial::new(f)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( multi_longsep(Partial::new(g)), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( multi(Partial::new(h)), Err(ErrMode::Incomplete(Needed::new(1))) ); } #[test] #[cfg(feature = "alloc")] fn repeat0_test() { fn multi(i: Partial<&[u8]>) -> IResult, 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::Backtrack(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_till0("abcd", "efgh").parse_peek(i) } let a = b"abcdabcdefghabcd"; let b = b"efghabcd"; let c = b"azerty"; let res_a = (vec![&b"abcd"[..], &b"abcd"[..]], &b"efgh"[..]); let res_b: (Vec<&[u8]>, &[u8]) = (Vec::new(), &b"efgh"[..]); assert_eq!(multi(&a[..]), Ok((&b"abcd"[..], res_a))); assert_eq!(multi(&b[..]), Ok((&b"abcd"[..], res_b))); assert_eq!( multi(&c[..]), Err(ErrMode::Backtrack(error_node_position!( &&c[..], ErrorKind::Many, error_position!(&&c[..], ErrorKind::Tag) ))) ); } #[test] #[cfg(feature = "std")] fn infinite_many() { fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> { println!("input: {:?}", input); Err(ErrMode::Backtrack(error_position!(&input, ErrorKind::Tag))) } // should not go into an infinite loop fn multi0(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { repeat(0.., unpeek(tst)).parse_peek(i) } let a = &b"abcdef"[..]; assert_eq!(multi0(a), Ok((a, Vec::new()))); fn multi1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> { repeat(1.., unpeek(tst)).parse_peek(i) } let a = &b"abcdef"[..]; assert_eq!( multi1(a), Err(ErrMode::Backtrack(error_position!(&a, ErrorKind::Tag))) ); } #[test] #[cfg(feature = "alloc")] fn repeat_test() { fn multi(i: Partial<&[u8]>) -> IResult, 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)] pub 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, _: 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]>> { fold_repeat(0.., "abcd", Vec::new, fold_into_vec).parse_peek(i) } assert_eq!( multi(Partial::new(&b"abcdef"[..])), Ok((Partial::new(&b"ef"[..]), vec![&b"abcd"[..]])) ); assert_eq!( multi(Partial::new(&b"abcdabcdefgh"[..])), Ok((Partial::new(&b"efgh"[..]), vec![&b"abcd"[..], &b"abcd"[..]])) ); assert_eq!( multi(Partial::new(&b"azerty"[..])), Ok((Partial::new(&b"azerty"[..]), Vec::new())) ); assert_eq!( multi(Partial::new(&b"abcdab"[..])), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( multi(Partial::new(&b"abcd"[..])), Err(ErrMode::Incomplete(Needed::new(4))) ); assert_eq!( multi(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(4))) ); } #[test] #[cfg(feature = "alloc")] #[cfg_attr(debug_assertions, should_panic)] fn fold_repeat0_empty_test() { fn fold_into_vec(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi_empty(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { fold_repeat(0.., "", Vec::new, fold_into_vec).parse_peek(i) } assert_eq!( multi_empty(Partial::new(&b"abcdef"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"abcdef"[..]), ErrorKind::Assert ))) ); } #[test] #[cfg(feature = "alloc")] fn fold_repeat1_test() { fn fold_into_vec(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { fold_repeat(1.., "abcd", Vec::new, fold_into_vec).parse_peek(i) } let a = &b"abcdef"[..]; let b = &b"abcdabcdefgh"[..]; let c = &b"azerty"[..]; let d = &b"abcdab"[..]; let res1 = vec![&b"abcd"[..]]; assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1))); let res2 = vec![&b"abcd"[..], &b"abcd"[..]]; assert_eq!( multi(Partial::new(b)), Ok((Partial::new(&b"efgh"[..]), res2)) ); assert_eq!( multi(Partial::new(c)), Err(ErrMode::Backtrack(error_position!( &Partial::new(c), ErrorKind::Many ))) ); assert_eq!( multi(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(2))) ); } #[test] #[cfg(feature = "alloc")] fn fold_repeat_test() { fn fold_into_vec(mut acc: Vec, item: T) -> Vec { acc.push(item); acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { fold_repeat(2..=4, "Abcd", Vec::new, fold_into_vec).parse_peek(i) } let a = &b"Abcdef"[..]; let b = &b"AbcdAbcdefgh"[..]; let c = &b"AbcdAbcdAbcdAbcdefgh"[..]; let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..]; let e = &b"AbcdAb"[..]; assert_eq!( multi(Partial::new(a)), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"ef"[..]), ErrorKind::Tag ))) ); let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]]; assert_eq!( multi(Partial::new(b)), Ok((Partial::new(&b"efgh"[..]), res1)) ); let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]]; assert_eq!( multi(Partial::new(c)), Ok((Partial::new(&b"efgh"[..]), res2)) ); let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]]; assert_eq!( multi(Partial::new(d)), Ok((Partial::new(&b"Abcdefgh"[..]), res3)) ); assert_eq!( multi(Partial::new(e)), Err(ErrMode::Incomplete(Needed::new(2))) ); } #[test] fn repeat0_count_test() { fn count0_nums(i: &[u8]) -> IResult<&[u8], usize> { repeat(0.., (digit, ",")).parse_peek(i) } assert_eq!(count0_nums(&b"123,junk"[..]), Ok((&b"junk"[..], 1))); assert_eq!(count0_nums(&b"123,45,junk"[..]), Ok((&b"junk"[..], 2))); assert_eq!( count0_nums(&b"1,2,3,4,5,6,7,8,9,0,junk"[..]), Ok((&b"junk"[..], 10)) ); assert_eq!(count0_nums(&b"hello"[..]), Ok((&b"hello"[..], 0))); } #[test] fn repeat1_count_test() { fn count1_nums(i: &[u8]) -> IResult<&[u8], usize> { repeat(1.., (digit, ",")).parse_peek(i) } assert_eq!(count1_nums(&b"123,45,junk"[..]), Ok((&b"junk"[..], 2))); assert_eq!( count1_nums(&b"1,2,3,4,5,6,7,8,9,0,junk"[..]), Ok((&b"junk"[..], 10)) ); assert_eq!( count1_nums(&b"hello"[..]), Err(ErrMode::Backtrack(error_position!( &&b"hello"[..], ErrorKind::Slice ))) ); } winnow-0.5.15/src/error.rs000064400000000000000000001171351046102023000135330ustar 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: `many0`, `alt`) //! - Can be modified according to the user's needs, because some languages need a lot more information //! - Help thread-through the [stream][crate::stream] //! //! To abstract these needs away from the user, generally `winnow` parsers use the [`PResult`] //! alias, rather than [`Result`][std::result::Result]. [`Parser::parse`] is a top-level operation //! that can help convert to a `Result` for integrating with your application's error reporting. //! //! Error types include: //! - `()` //! - [`ErrorKind`] //! - [`InputError`] (mostly for testing) //! - [`ContextError`] //! - [`TreeError`] (mostly for testing) //! - [Custom errors][crate::_topic::error] #[cfg(feature = "alloc")] use crate::lib::std::borrow::ToOwned; use crate::lib::std::fmt; use core::num::NonZeroUsize; use crate::stream::AsBStr; use crate::stream::Stream; #[allow(unused_imports)] // Here for intra-doc links use crate::Parser; /// Holds the result of [`Parser`] /// /// - `Ok((I, O))` is the remaining [input][crate::stream] and the parsed value /// - [`Err(ErrMode)`][ErrMode] is the error along with how to respond to it /// /// By default, the error type (`E`) is [`InputError`] /// /// [`Parser::parse`] is a top-level operation that can help convert to a `Result` for integrating /// with your application's error reporting. pub type IResult> = PResult<(I, O), E>; /// Holds the result of [`Parser`] /// /// - `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 [`ErrorKind`]. /// /// [`Parser::parse`] is a top-level operation that can help convert to a `Result` for integrating /// with your application's error reporting. pub type PResult = Result>; /// Contains information on needed data if a parser returned `Incomplete` /// /// **Note:** This is only possible for `Stream` that are [partial][`crate::stream::StreamIsPartial`], /// like [`Partial`][crate::Partial]. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub enum Needed { /// Needs more data, but we do not know how much Unknown, /// Contains the required data size in bytes Size(NonZeroUsize), } impl Needed { /// Creates `Needed` instance, returns `Needed::Unknown` if the argument is zero pub fn new(s: usize) -> Self { match NonZeroUsize::new(s) { Some(sz) => Needed::Size(sz), None => Needed::Unknown, } } /// Indicates if we know how many bytes we need pub fn is_known(&self) -> bool { *self != Needed::Unknown } /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value. #[inline] pub fn map usize>(self, f: F) -> Needed { match self { Needed::Unknown => Needed::Unknown, Needed::Size(n) => Needed::new(f(n)), } } } /// Add parse error state to [`ParserError`]s #[derive(Debug, Clone, PartialEq)] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] pub enum ErrMode { /// 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`][crate::stream::Stream] is [partial][`crate::stream::StreamIsPartial`], like with /// [`Partial`][crate::Partial] /// /// Convert this into an `Backtrack` with [`Parser::complete_err`] Incomplete(Needed), /// The parser failed with a recoverable error (the default). /// /// For example, a parser for json values might include a /// [`dec_uint`][crate::ascii::dec_uint] as one case in an [`alt`][crate::combinator::alt] /// combiantor. If it fails, the next case should be tried. Backtrack(E), /// The parser had an unrecoverable error. /// /// The parser was on the right branch, so directly report it to the user rather than trying /// other branches. You can use [`cut_err()`][crate::combinator::cut_err] combinator to switch /// from `ErrMode::Backtrack` to `ErrMode::Cut`. /// /// For example, one case in an [`alt`][crate::combinator::alt] combinator found a unique prefix /// and you want any further errors parsing the case to be reported to the user. Cut(E), } impl 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`] #[cfg_attr(debug_assertions, track_caller)] #[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)) } #[cfg_attr(debug_assertions, track_caller)] #[inline(always)] fn assert(input: &I, message: &'static str) -> Self where I: crate::lib::std::fmt::Debug, { ErrMode::Backtrack(E::assert(input, message)) } #[inline] fn append(self, input: &I, kind: ErrorKind) -> Self { match self { ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, kind)), e => e, } } fn or(self, other: Self) -> Self { match (self, other) { (ErrMode::Backtrack(e), ErrMode::Backtrack(o)) => ErrMode::Backtrack(e.or(o)), (ErrMode::Incomplete(e), _) | (_, ErrMode::Incomplete(e)) => ErrMode::Incomplete(e), (ErrMode::Cut(e), _) | (_, ErrMode::Cut(e)) => ErrMode::Cut(e), } } } impl 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, ctx: C) -> Self { self.map(|err| err.add_context(input, ctx)) } } 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 {} bytes/chars", u), ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"), ErrMode::Cut(c) => write!(f, "Parsing Failure: {:?}", c), ErrMode::Backtrack(c) => write!(f, "Parsing Error: {:?}", c), } } } /// The basic [`Parser`] trait for errors /// /// It provides methods to create an error from some combinators, /// and combine existing errors in combinators like `alt`. pub trait ParserError: Sized { /// Creates an error from the input position and an [`ErrorKind`] fn from_error_kind(input: &I, kind: ErrorKind) -> Self; /// Process a parser assertion #[cfg_attr(debug_assertions, track_caller)] fn assert(input: &I, _message: &'static str) -> Self where I: crate::lib::std::fmt::Debug, { #[cfg(debug_assertions)] panic!("assert `{}` failed at {:#?}", _message, input); #[cfg(not(debug_assertions))] Self::from_error_kind(input, ErrorKind::Assert) } /// Like [`ParserError::from_error_kind`] but merges it with the existing error. /// /// This is useful when backtracking through a parse tree, accumulating error context on the /// way. fn append(self, input: &I, kind: ErrorKind) -> Self; /// Combines errors from two different parse branches. /// /// For example, this would be used by [`alt`][crate::combinator::alt] to report the error from /// each case. #[inline] fn or(self, other: Self) -> Self { other } } /// Used by [`Parser::context`] to add custom data to error while backtracking /// /// May be implemented multiple times for different kinds of context. pub trait AddContext: Sized { /// Append to an existing error custom data /// /// This is used mainly by [`Parser::context`], to add user friendly information /// to errors when backtracking through a parse tree #[inline] fn add_context(self, _input: &I, _ctx: C) -> Self { self } } /// Create a new error with an external error, from [`std::str::FromStr`] /// /// This trait is required by the [`Parser::try_map`] combinator. pub trait FromExternalError { /// 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] pub fn new(input: I, kind: ErrorKind) -> Self { Self { input, kind } } /// 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<'i, I: ToOwned> InputError<&'i 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, _: &I, _: ErrorKind) -> Self { self } } impl AddContext for InputError {} 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 { } impl ParserError for () { #[inline] fn from_error_kind(_: &I, _: ErrorKind) -> Self {} #[inline] fn append(self, _: &I, _: ErrorKind) -> Self {} } impl AddContext for () {} 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, _kind: ErrorKind) -> Self { self } #[inline] fn or(self, other: Self) -> Self { other } } impl AddContext for ContextError { #[inline] fn add_context(mut self, _input: &I, ctx: C) -> Self { #[cfg(feature = "alloc")] self.context.push(ctx); self } } #[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(()) } } /// 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 ::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: 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: Clone, { fn from_error_kind(input: &I, kind: ErrorKind) -> Self { TreeError::Base(TreeErrorBase { input: input.clone(), kind, cause: None, }) } fn append(self, input: &I, kind: ErrorKind) -> Self { let frame = TreeErrorFrame::Kind(TreeErrorBase { input: input.clone(), kind, cause: None, }); self.append_frame(frame) } fn or(self, other: Self) -> Self { match (self, other) { (TreeError::Alt(mut first), TreeError::Alt(second)) => { // Just in case an implementation does a divide-and-conquer algorithm // // To prevent mixing `alt`s at different levels, parsers should // `alt_err.append(input, ErrorKind::Alt)`. first.extend(second); TreeError::Alt(first) } (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => { alt.push(new); TreeError::Alt(alt) } (first, second) => TreeError::Alt(vec![first, second]), } } } #[cfg(feature = "std")] impl AddContext for TreeError where I: Clone, { fn add_context(self, input: &I, context: C) -> Self { let frame = TreeErrorFrame::Context(TreeErrorContext { input: input.clone(), context, }); self.append_frame(frame) } } #[cfg(feature = "std")] impl FromExternalError for TreeError where I: Clone, { fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self { TreeError::Base(TreeErrorBase { input: input.clone(), kind, cause: Some(Box::new(e)), }) } } #[cfg(feature = "std")] impl TreeError where I: Clone + 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: 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, replaced with [`ContextError`] #[cfg(feature = "std")] #[allow(deprecated)] #[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")] #[derive(Clone, Debug, Eq, PartialEq)] pub struct VerboseError { /// Accumulated error information pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind)>, } #[cfg(feature = "std")] #[allow(deprecated)] impl<'i, I: ToOwned, C> VerboseError<&'i I, C> where ::Owned: Clone, { /// Obtaining ownership pub fn into_owned(self) -> VerboseError<::Owned, C> { self.map_input(ToOwned::to_owned) } } #[cfg(feature = "std")] #[allow(deprecated)] impl VerboseError { /// Translate the input type pub fn map_input(self, op: O) -> VerboseError where O: Fn(I) -> I2, { VerboseError { errors: self.errors.into_iter().map(|(i, k)| (op(i), k)).collect(), } } } /// Deprecated, replaced with [`ContextError`] #[cfg(feature = "std")] #[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")] #[derive(Clone, Debug, Eq, PartialEq)] pub enum VerboseErrorKind { /// Static string added by the `context` function Context(C), /// Error kind given by various parsers Winnow(ErrorKind), } #[cfg(feature = "std")] #[allow(deprecated)] impl ParserError for VerboseError { fn from_error_kind(input: &I, kind: ErrorKind) -> Self { VerboseError { errors: vec![(input.clone(), VerboseErrorKind::Winnow(kind))], } } fn append(mut self, input: &I, kind: ErrorKind) -> Self { self.errors .push((input.clone(), VerboseErrorKind::Winnow(kind))); self } } #[cfg(feature = "std")] #[allow(deprecated)] impl AddContext for VerboseError { fn add_context(mut self, input: &I, ctx: C) -> Self { self.errors .push((input.clone(), VerboseErrorKind::Context(ctx))); self } } #[cfg(feature = "std")] #[allow(deprecated)] impl FromExternalError for VerboseError { /// Create a new error from an input position and an external error fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self { Self::from_error_kind(input, kind) } } #[cfg(feature = "std")] #[allow(deprecated)] impl fmt::Display for VerboseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Parse error:")?; for (input, error) in &self.errors { match error { VerboseErrorKind::Winnow(e) => writeln!(f, "{} at: {}", e.description(), input)?, VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?, } } Ok(()) } } #[cfg(feature = "std")] #[allow(deprecated)] impl< I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static, C: fmt::Display + fmt::Debug, > std::error::Error for VerboseError { } /// Provide some minor debug context for errors #[rustfmt::skip] #[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)] #[allow(missing_docs)] pub enum ErrorKind { Assert, Token, Tag, Alt, Many, Eof, Slice, Complete, Not, Verify, Fail, } impl ErrorKind { #[rustfmt::skip] /// Converts an `ErrorKind` to a text description pub fn description(&self) -> &str { match *self { ErrorKind::Assert => "assert", ErrorKind::Token => "token", ErrorKind::Tag => "tag", ErrorKind::Alt => "alternative", ErrorKind::Many => "many", ErrorKind::Eof => "end of file", ErrorKind::Slice => "slice", ErrorKind::Complete => "complete", ErrorKind::Not => "negation", ErrorKind::Verify => "predicate verification", ErrorKind::Fail => "fail", } } } impl ParserError for ErrorKind { #[inline] fn from_error_kind(_input: &I, kind: ErrorKind) -> Self { kind } #[inline] fn append(self, _: &I, _: 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 #[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 {}, column {}", line_num, col_num)?; // | for _ in 0..=gutter { write!(f, " ")?; } writeln!(f, "|")?; // 1 | 00:32:00.a999999 write!(f, "{} | ", line_num)?; writeln!(f, "{}", String::from_utf8_lossy(content))?; // | ^ for _ in 0..=gutter { write!(f, " ")?; } write!(f, "|")?; for _ in 0..=col_idx { write!(f, " ")?; } // The span will be empty at eof, so we need to make sure we always print at least // one `^` write!(f, "^")?; for _ in (span_start + 1)..(span_end.min(span_start + content.len())) { write!(f, "^")?; } writeln!(f)?; } else { let content = input; writeln!(f, "{}", String::from_utf8_lossy(content))?; for _ in 0..=span_start { write!(f, " ")?; } // The span will be empty at eof, so we need to make sure we always print at least // one `^` write!(f, "^")?; for _ in (span_start + 1)..(span_end.min(span_start + content.len())) { write!(f, "^")?; } writeln!(f)?; } write!(f, "{}", self.inner)?; Ok(()) } } #[cfg(feature = "std")] fn translate_position(input: &[u8], index: usize) -> (usize, usize) { if input.is_empty() { return (0, index); } let safe_index = index.min(input.len() - 1); let column_offset = index - safe_index; let index = safe_index; let nl = input[0..index] .iter() .rev() .enumerate() .find(|(_, b)| **b == b'\n') .map(|(nl, _)| index - nl - 1); let line_start = match nl { Some(nl) => nl + 1, None => 0, }; let line = input[0..line_start].iter().filter(|b| **b == b'\n').count(); let line = line; // HACK: This treats byte offset and column offsets the same let column = std::str::from_utf8(&input[line_start..=index]) .map(|s| s.chars().count() - 1) .unwrap_or_else(|_| index - line_start); let column = column + column_offset; (line, column) } #[cfg(test)] #[cfg(feature = "std")] mod test_translate_position { use super::*; #[test] fn empty() { let input = b""; let index = 0; let position = translate_position(&input[..], index); assert_eq!(position, (0, 0)); } #[test] fn start() { let input = b"Hello"; let index = 0; let position = translate_position(&input[..], index); assert_eq!(position, (0, 0)); } #[test] fn end() { let input = b"Hello"; let index = input.len() - 1; let position = translate_position(&input[..], index); assert_eq!(position, (0, input.len() - 1)); } #[test] fn after() { let input = b"Hello"; let index = input.len(); let position = translate_position(&input[..], index); assert_eq!(position, (0, input.len())); } #[test] fn first_line() { let input = b"Hello\nWorld\n"; let index = 2; let position = translate_position(&input[..], index); assert_eq!(position, (0, 2)); } #[test] fn end_of_line() { let input = b"Hello\nWorld\n"; let index = 5; let position = translate_position(&input[..], index); assert_eq!(position, (0, 5)); } #[test] fn start_of_second_line() { let input = b"Hello\nWorld\n"; let index = 6; let position = translate_position(&input[..], index); assert_eq!(position, (1, 0)); } #[test] fn second_line() { let input = b"Hello\nWorld\n"; let index = 8; let position = translate_position(&input[..], index); assert_eq!(position, (1, 2)); } } /// Creates a parse error from a [`ErrorKind`] /// and the position in the input #[cfg(test)] macro_rules! error_position( ($input:expr, $code:expr) => ({ $crate::error::ParserError::from_error_kind($input, $code) }); ); #[cfg(test)] macro_rules! error_node_position( ($input:expr, $code:expr, $next:expr) => ({ $crate::error::ParserError::append($next, $input, $code) }); ); winnow-0.5.15/src/lib.rs000064400000000000000000000167541046102023000131550ustar 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) //! //! ## 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)] // BEGIN - Embark standard lints v6 for Rust 1.55+ // do not change or add/remove here, but one can add exceptions after this section // for more info see: // "-Dunsafe_code", #![warn(clippy::all)] #![warn(clippy::await_holding_lock)] #![warn(clippy::char_lit_as_u8)] #![warn(clippy::checked_conversions)] #![warn(clippy::dbg_macro)] #![warn(clippy::debug_assert_with_mut_call)] #![warn(clippy::doc_markdown)] #![warn(clippy::empty_enum)] #![warn(clippy::enum_glob_use)] #![warn(clippy::exit)] #![warn(clippy::expl_impl_clone_on_copy)] #![warn(clippy::explicit_deref_methods)] #![warn(clippy::explicit_into_iter_loop)] #![warn(clippy::fallible_impl_from)] #![warn(clippy::filter_map_next)] #![warn(clippy::flat_map_option)] #![warn(clippy::float_cmp_const)] #![warn(clippy::fn_params_excessive_bools)] #![warn(clippy::from_iter_instead_of_collect)] #![warn(clippy::if_let_mutex)] #![warn(clippy::implicit_clone)] #![warn(clippy::imprecise_flops)] #![warn(clippy::inefficient_to_string)] #![warn(clippy::invalid_upcast_comparisons)] #![warn(clippy::large_digit_groups)] #![warn(clippy::large_stack_arrays)] #![warn(clippy::large_types_passed_by_value)] #![warn(clippy::let_unit_value)] #![warn(clippy::linkedlist)] #![warn(clippy::lossy_float_literal)] #![warn(clippy::macro_use_imports)] #![warn(clippy::manual_ok_or)] #![warn(clippy::map_err_ignore)] #![warn(clippy::map_flatten)] #![warn(clippy::map_unwrap_or)] #![warn(clippy::match_on_vec_items)] #![warn(clippy::match_same_arms)] #![warn(clippy::match_wild_err_arm)] #![warn(clippy::match_wildcard_for_single_variants)] #![warn(clippy::mem_forget)] #![warn(clippy::mismatched_target_os)] #![warn(clippy::missing_enforced_import_renames)] #![warn(clippy::mut_mut)] #![warn(clippy::mutex_integer)] #![warn(clippy::needless_borrow)] #![warn(clippy::needless_continue)] #![warn(clippy::needless_for_each)] #![warn(clippy::option_option)] #![warn(clippy::path_buf_push_overwrite)] #![warn(clippy::ptr_as_ptr)] #![warn(clippy::rc_mutex)] #![warn(clippy::ref_option_ref)] #![warn(clippy::rest_pat_in_fully_bound_structs)] #![warn(clippy::same_functions_in_if_condition)] #![warn(clippy::semicolon_if_nothing_returned)] #![warn(clippy::single_match_else)] #![warn(clippy::string_add_assign)] #![warn(clippy::string_add)] #![warn(clippy::string_lit_as_bytes)] #![warn(clippy::string_to_string)] #![warn(clippy::todo)] #![warn(clippy::trait_duplication_in_bounds)] #![warn(clippy::unimplemented)] #![warn(clippy::unnested_or_patterns)] #![warn(clippy::unused_self)] #![warn(clippy::useless_transmute)] #![warn(clippy::verbose_file_reads)] #![warn(clippy::zero_sized_map_values)] #![warn(future_incompatible)] #![warn(nonstandard_style)] #![warn(rust_2018_idioms)] // END - Embark standard lints v6 for Rust 1.55+ #![allow(clippy::branches_sharing_code)] #![allow(clippy::collapsible_else_if)] #![allow(clippy::if_same_then_else)] #![allow(clippy::bool_assert_comparison)] #![allow(clippy::let_and_return)] #![allow(clippy::assertions_on_constants)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::single_match_else)] #![allow(clippy::single_match)] #![allow(clippy::unnested_or_patterns)] #[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))] #[cfg(feature = "alloc")] #[cfg_attr(test, macro_use)] extern crate alloc; #[cfg(doctest)] extern crate doc_comment; #[cfg(doctest)] doc_comment::doctest!("../README.md"); /// Lib module to re-export everything needed from `std` or `core`/`alloc`. This is how `serde` does /// it, albeit there it is not public. #[doc(hidden)] pub(crate) mod lib { /// `std` facade allowing `std`/`core` to be interchangeable. Reexports `alloc` crate optionally, /// as well as `core` or `std` #[cfg(not(feature = "std"))] /// internal std exports for no_std compatibility pub mod std { #[doc(hidden)] #[cfg(not(feature = "alloc"))] pub use core::borrow; #[cfg(feature = "alloc")] #[doc(hidden)] pub use alloc::{borrow, boxed, collections, string, vec}; #[doc(hidden)] pub use core::{cmp, convert, fmt, hash, iter, mem, ops, option, result, slice, str}; /// internal reproduction of std prelude #[doc(hidden)] pub mod prelude { pub use core::prelude as v1; } } #[cfg(feature = "std")] /// internal std exports for `no_std` compatibility pub mod std { #[doc(hidden)] pub use std::{ alloc, borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, ops, option, result, slice, str, string, vec, }; /// internal reproduction of std prelude #[doc(hidden)] pub mod prelude { pub use std::prelude as v1; } } } #[macro_use] mod macros; #[macro_use] pub mod error; mod parser; pub mod stream; pub mod ascii; pub mod binary; pub mod combinator; pub mod token; pub mod trace; #[cfg(feature = "unstable-doc")] pub mod _topic; #[cfg(feature = "unstable-doc")] pub mod _tutorial; /// Core concepts available for glob import /// /// Including /// - [`StreamIsPartial`][crate::stream::StreamIsPartial] /// - [`Parser`] /// /// ## Example /// /// ```rust /// use winnow::prelude::*; /// /// fn parse_data(input: &mut &str) -> PResult { /// // ... /// # winnow::ascii::dec_uint(input) /// } /// /// fn main() { /// let result = parse_data.parse("100"); /// assert_eq!(result, Ok(100)); /// } /// ``` pub mod prelude { pub use crate::stream::StreamIsPartial as _; pub use crate::IResult; pub use crate::PResult; pub use crate::Parser; } pub use error::IResult; pub use error::PResult; pub use parser::*; pub use stream::BStr; pub use stream::Bytes; pub use stream::Located; pub use stream::Partial; pub use stream::Stateful; pub use stream::Str; winnow-0.5.15/src/macros.rs000064400000000000000000000062351046102023000136640ustar 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::success; /// # 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' => success('\u{8}'), /// 'f' => success('\u{c}'), /// 'n' => success('\n'), /// 'r' => success('\r'), /// 't' => success('\t'), /// '\\' => success('\\'), /// '"' => success('"'), /// _ => fail::<_, char, _>, /// } /// .parse_next(input) /// } /// /// assert_eq!(escaped.parse_peek("\\nHello"), Ok(("Hello", '\n'))); /// ``` #[macro_export] #[doc(hidden)] // forced to be visible in intended location macro_rules! dispatch { ($match_parser: expr; $( $pat:pat $(if $pred:expr)? => $expr: expr ),+ $(,)? ) => { $crate::trace::trace("dispatch", move |i: &mut _| { use $crate::Parser; let initial = $match_parser.parse_next(i)?; match initial { $( $pat $(if $pred)? => $expr.parse_next(i), )* } }) } } macro_rules! succ ( (0, $submac:ident ! ($($rest:tt)*)) => ($submac!(1, $($rest)*)); (1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*)); (2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*)); (3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*)); (4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*)); (5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*)); (6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*)); (7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*)); (8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*)); (9, $submac:ident ! ($($rest:tt)*)) => ($submac!(10, $($rest)*)); (10, $submac:ident ! ($($rest:tt)*)) => ($submac!(11, $($rest)*)); (11, $submac:ident ! ($($rest:tt)*)) => ($submac!(12, $($rest)*)); (12, $submac:ident ! ($($rest:tt)*)) => ($submac!(13, $($rest)*)); (13, $submac:ident ! ($($rest:tt)*)) => ($submac!(14, $($rest)*)); (14, $submac:ident ! ($($rest:tt)*)) => ($submac!(15, $($rest)*)); (15, $submac:ident ! ($($rest:tt)*)) => ($submac!(16, $($rest)*)); (16, $submac:ident ! ($($rest:tt)*)) => ($submac!(17, $($rest)*)); (17, $submac:ident ! ($($rest:tt)*)) => ($submac!(18, $($rest)*)); (18, $submac:ident ! ($($rest:tt)*)) => ($submac!(19, $($rest)*)); (19, $submac:ident ! ($($rest:tt)*)) => ($submac!(20, $($rest)*)); (20, $submac:ident ! ($($rest:tt)*)) => ($submac!(21, $($rest)*)); ); winnow-0.5.15/src/parser.rs000064400000000000000000000774211046102023000137010ustar 00000000000000//! Basic types to build the parsers use crate::combinator::*; use crate::error::{AddContext, FromExternalError, IResult, PResult, ParseError, ParserError}; use crate::stream::{AsChar, Compare, Location, ParseSlice, Stream, StreamIsPartial}; /// Core trait for parsing /// /// The simplest way to implement a `Parser` is with a function /// ```rust /// use winnow::prelude::*; /// /// fn success(input: &mut &str) -> PResult<()> { /// let output = (); /// Ok(output) /// } /// /// let (input, output) = success.parse_peek("Hello").unwrap(); /// assert_eq!(input, "Hello"); // We didn't consume any input /// ``` /// /// which can be made stateful by returning a function /// ```rust /// use winnow::prelude::*; /// /// fn success(output: O) -> impl FnMut(&mut &str) -> PResult { /// move |input: &mut &str| { /// let output = output.clone(); /// Ok(output) /// } /// } /// /// let (input, output) = success("World").parse_peek("Hello").unwrap(); /// assert_eq!(input, "Hello"); // We didn't consume any input /// assert_eq!(output, "World"); /// ``` /// /// Additionally, some basic types implement `Parser` as well, including /// - `u8` and `char`, see [`winnow::token::one_of`][crate::token::one_of] /// - `&[u8]` and `&str`, see [`winnow::token::tag`][crate::token::tag] pub trait Parser { /// 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, I: Clone, 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 includes advancing the [`Stream`] to the next location. #[inline(always)] fn parse_peek(&mut self, mut input: I) -> IResult { match self.parse_next(&mut input) { Ok(o) => Ok((input, o)), Err(err) => Err(err), } } /// Treat `&mut Self` as a parser /// /// This helps when needing to move a `Parser` when all you have is a `&mut Parser`. /// /// # Example /// /// Because parsers are `FnMut`, they can be called multiple times. This prevents moving `f` /// into [`length_data`][crate::binary::length_data] and `g` into /// [`Parser::complete_err`]: /// ```rust,compile_fail /// # use winnow::prelude::*; /// # use winnow::Parser; /// # use winnow::error::ParserError; /// # use winnow::binary::length_data; /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>( /// mut f: impl Parser<&'i [u8], usize, E>, /// mut g: impl Parser<&'i [u8], O, E> /// ) -> impl Parser<&'i [u8], O, E> { /// move |i: &mut &'i [u8]| { /// let mut data = length_data(f).parse_next(i)?; /// let o = g.complete_err().parse_next(&mut data)?; /// Ok(o) /// } /// } /// ``` /// /// By adding `by_ref`, we can make this work: /// ```rust /// # use winnow::prelude::*; /// # use winnow::Parser; /// # use winnow::error::ParserError; /// # use winnow::binary::length_data; /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>( /// mut f: impl Parser<&'i [u8], usize, E>, /// mut g: impl Parser<&'i [u8], O, E> /// ) -> impl Parser<&'i [u8], O, E> { /// move |i: &mut &'i [u8]| { /// let mut data = length_data(f.by_ref()).parse_next(i)?; /// let o = g.by_ref().complete_err().parse_next(&mut data)?; /// Ok(o) /// } /// } /// ``` #[inline(always)] fn by_ref(&mut self) -> ByRef<'_, Self> where Self: core::marker::Sized, { ByRef::new(self) } /// Produce the provided value /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::alpha1; /// # fn main() { /// /// let mut parser = alpha1.value(1234); /// /// assert_eq!(parser.parse_peek("abcd"), Ok(("", 1234))); /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); /// # } /// ``` #[doc(alias = "to")] #[inline(always)] fn value(self, val: O2) -> Value where Self: core::marker::Sized, O2: Clone, { Value::new(self, val) } /// Discards the output of the `Parser` /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::alpha1; /// # fn main() { /// /// let mut parser = alpha1.void(); /// /// assert_eq!(parser.parse_peek("abcd"), Ok(("", ()))); /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); /// # } /// ``` #[inline(always)] fn void(self) -> Void where Self: core::marker::Sized, { Void::new(self) } /// Convert the parser's output to another type using [`std::convert::From`] /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::error::InputError; /// use winnow::ascii::alpha1; /// # fn main() { /// /// fn parser1<'s>(i: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alpha1(i) /// } /// /// let mut parser2 = parser1.output_into(); /// /// // the parser converts the &str output of the child parser into a Vec /// let bytes: IResult<&str, Vec> = parser2.parse_peek("abcd"); /// assert_eq!(bytes, Ok(("", vec![97, 98, 99, 100]))); /// # } /// ``` #[inline(always)] fn output_into(self) -> OutputInto where Self: core::marker::Sized, O: Into, { OutputInto::new(self) } /// Produce the consumed input as produced value. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::{alpha1}; /// use winnow::combinator::separated_pair; /// # fn main() { /// /// let mut parser = separated_pair(alpha1, ',', alpha1).recognize(); /// /// assert_eq!(parser.parse_peek("abcd,efgh"), Ok(("", "abcd,efgh"))); /// assert_eq!(parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify)))); /// # } /// ``` #[doc(alias = "concat")] #[inline(always)] fn recognize(self) -> Recognize where Self: core::marker::Sized, I: Stream, { Recognize::new(self) } /// Produce the consumed input with the output /// /// Functions similarly to [recognize][Parser::recognize] except it /// returns the parser output as well. /// /// This can be useful especially in cases where the output is not the same type /// as the input, or the input is a user defined type. /// /// Returned tuple is of the format `(produced output, consumed input)`. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError}; /// use winnow::ascii::{alpha1}; /// use winnow::token::tag; /// use winnow::combinator::separated_pair; /// /// fn inner_parser<'s>(input: &mut &'s str) -> PResult> { /// "1234".value(true).parse_next(input) /// } /// /// let mut consumed_parser = separated_pair(alpha1, ',', alpha1).value(true).with_recognized(); /// /// assert_eq!(consumed_parser.parse_peek("abcd,efgh1"), Ok(("1", (true, "abcd,efgh")))); /// assert_eq!(consumed_parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify)))); /// /// // the second output (representing the consumed input) /// // should be the same as that of the `recognize` parser. /// let mut recognize_parser = inner_parser.recognize(); /// let mut consumed_parser = inner_parser.with_recognized().map(|(output, consumed)| consumed); /// /// assert_eq!(recognize_parser.parse_peek("1234"), consumed_parser.parse_peek("1234")); /// assert_eq!(recognize_parser.parse_peek("abcd"), consumed_parser.parse_peek("abcd")); /// ``` #[doc(alias = "consumed")] #[inline(always)] fn with_recognized(self) -> WithRecognized where Self: core::marker::Sized, I: Stream, { WithRecognized::new(self) } /// Produce the location of the consumed input as produced value. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream}; /// use winnow::stream::Located; /// use winnow::ascii::alpha1; /// use winnow::combinator::separated_pair; /// /// let mut parser = separated_pair(alpha1.span(), ',', alpha1.span()); /// /// assert_eq!(parser.parse(Located::new("abcd,efgh")), Ok((0..4, 5..9))); /// assert_eq!(parser.parse_peek(Located::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(Located::new("abcd;").peek_slice(4).0, ErrorKind::Verify)))); /// ``` #[inline(always)] fn span(self) -> Span where Self: core::marker::Sized, I: Stream + Location, { Span::new(self) } /// Produce the location of consumed input with the output /// /// Functions similarly to [`Parser::span`] except it /// returns the parser output as well. /// /// This can be useful especially in cases where the output is not the same type /// as the input, or the input is a user defined type. /// /// Returned tuple is of the format `(produced output, consumed input)`. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream}; /// use winnow::stream::Located; /// use winnow::ascii::alpha1; /// use winnow::token::tag; /// use winnow::combinator::separated_pair; /// /// fn inner_parser<'s>(input: &mut Located<&'s str>) -> PResult>> { /// "1234".value(true).parse_next(input) /// } /// /// # fn main() { /// /// let mut consumed_parser = separated_pair(alpha1.value(1).with_span(), ',', alpha1.value(2).with_span()); /// /// assert_eq!(consumed_parser.parse(Located::new("abcd,efgh")), Ok(((1, 0..4), (2, 5..9)))); /// assert_eq!(consumed_parser.parse_peek(Located::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(Located::new("abcd;").peek_slice(4).0, ErrorKind::Verify)))); /// /// // the second output (representing the consumed input) /// // should be the same as that of the `span` parser. /// let mut recognize_parser = inner_parser.span(); /// let mut consumed_parser = inner_parser.with_span().map(|(output, consumed)| consumed); /// /// assert_eq!(recognize_parser.parse_peek(Located::new("1234")), consumed_parser.parse_peek(Located::new("1234"))); /// assert_eq!(recognize_parser.parse_peek(Located::new("abcd")), consumed_parser.parse_peek(Located::new("abcd"))); /// # } /// ``` #[inline(always)] fn with_span(self) -> WithSpan where Self: core::marker::Sized, I: Stream + Location, { WithSpan::new(self) } /// Maps a function over the output of a parser /// /// # Example /// /// ```rust /// use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::digit1; /// # fn main() { /// /// let mut parser = digit1.map(|s: &str| s.len()); /// /// // the parser will count how many characters were returned by digit1 /// assert_eq!(parser.parse_peek("123456"), Ok(("", 6))); /// /// // this will fail if digit1 fails /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice)))); /// # } /// ``` #[inline(always)] fn map(self, map: G) -> Map where G: Fn(O) -> O2, Self: core::marker::Sized, { Map::new(self, map) } /// Applies a function returning a `Result` over the output of a parser. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::digit1; /// # fn main() { /// /// let mut parse = digit1.try_map(|s: &str| s.parse::()); /// /// // 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) -> TryMap where Self: core::marker::Sized, G: FnMut(O) -> Result, I: Stream, E: FromExternalError, { TryMap::new(self, map) } /// Apply both [`Parser::verify`] and [`Parser::map`]. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::digit1; /// # fn main() { /// /// let mut parse = digit1.verify_map(|s: &str| s.parse::().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) -> VerifyMap where Self: core::marker::Sized, G: FnMut(O) -> Option, I: Stream, E: ParserError, { VerifyMap::new(self, map) } /// Creates a parser from the output of this one /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser}; /// use winnow::token::take; /// use winnow::binary::u8; /// /// fn length_data<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// u8.flat_map(take).parse_next(input) /// } /// /// assert_eq!(length_data.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..]))); /// assert_eq!(length_data.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice)))); /// ``` /// /// which is the same as /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser}; /// use winnow::token::take; /// use winnow::binary::u8; /// /// fn length_data<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// let length = u8.parse_next(input)?; /// let data = take(length).parse_next(input)?; /// Ok(data) /// } /// /// assert_eq!(length_data.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..]))); /// assert_eq!(length_data.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice)))); /// ``` #[inline(always)] fn flat_map(self, map: G) -> FlatMap where Self: core::marker::Sized, G: FnMut(O) -> H, H: Parser, { FlatMap::new(self, map) } /// Applies a second parser over the output of the first one /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::digit1; /// use winnow::token::take; /// # fn main() { /// /// let mut digits = take(5u8).and_then(digit1); /// /// assert_eq!(digits.parse_peek("12345"), Ok(("", "12345"))); /// assert_eq!(digits.parse_peek("123ab"), Ok(("", "123"))); /// assert_eq!(digits.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Slice)))); /// # } /// ``` #[inline(always)] fn and_then(self, inner: G) -> AndThen where Self: core::marker::Sized, G: Parser, O: StreamIsPartial, I: Stream, { AndThen::new(self, inner) } /// Apply [`std::str::FromStr`] to the output of the parser /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// use winnow::ascii::digit1; /// /// fn parser<'s>(input: &mut &'s str) -> PResult> { /// 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) -> ParseTo where Self: core::marker::Sized, I: Stream, O: ParseSlice, E: ParserError, { ParseTo::new(self) } /// Returns the output of the child parser if it satisfies a verification function. /// /// The verification function takes as argument a reference to the output of the /// parser. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser}; /// # use winnow::ascii::alpha1; /// # fn main() { /// /// let mut parser = alpha1.verify(|s: &str| s.len() == 4); /// /// assert_eq!(parser.parse_peek("abcd"), Ok(("", "abcd"))); /// assert_eq!(parser.parse_peek("abcde"), Err(ErrMode::Backtrack(InputError::new("abcde", ErrorKind::Verify)))); /// assert_eq!(parser.parse_peek("123abcd;"),Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice)))); /// # } /// ``` #[doc(alias = "satisfy")] #[doc(alias = "filter")] #[inline(always)] fn verify(self, filter: G) -> Verify where Self: core::marker::Sized, G: Fn(&O2) -> bool, I: Stream, O: crate::lib::std::borrow::Borrow, O2: ?Sized, E: ParserError, { Verify::new(self, filter) } /// If parsing fails, add context to the error /// /// This is used mainly to add user friendly information /// to errors when backtracking through a parse tree. #[doc(alias = "labelled")] #[inline(always)] fn context(self, context: C) -> Context where Self: core::marker::Sized, I: Stream, E: AddContext, C: Clone + crate::lib::std::fmt::Debug, { Context::new(self, context) } /// Transforms [`Incomplete`][crate::error::ErrMode::Incomplete] into [`Backtrack`][crate::error::ErrMode::Backtrack] /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, stream::Partial, Parser}; /// # use winnow::token::take; /// # fn main() { /// /// let mut parser = take(5u8).complete_err(); /// /// assert_eq!(parser.parse_peek(Partial::new("abcdefg")), Ok((Partial::new("fg"), "abcde"))); /// assert_eq!(parser.parse_peek(Partial::new("abcd")), Err(ErrMode::Backtrack(InputError::new(Partial::new("abcd"), ErrorKind::Complete)))); /// # } /// ``` #[inline(always)] fn complete_err(self) -> CompleteErr where Self: core::marker::Sized, { CompleteErr::new(self) } /// Convert the parser's error to another type using [`std::convert::From`] #[inline(always)] fn err_into(self) -> ErrInto where Self: core::marker::Sized, E: Into, { ErrInto::new(self) } } impl<'a, I, O, E, F> Parser for F where F: FnMut(&mut I) -> PResult + 'a, 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::Verify)))); /// assert_eq!(parser.parse_peek(&b"bc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"bc"[..], ErrorKind::Verify)))); /// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Token)))); /// ``` impl Parser for u8 where I: StreamIsPartial, I: Stream, E: ParserError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { crate::token::one_of(*self).parse_next(i) } } /// This is a shortcut for [`one_of`][crate::token::one_of]. /// /// # Example /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{ErrorKind, InputError}}; /// fn parser<'s>(i: &mut &'s str) -> PResult> { /// 'a'.parse_next(i) /// } /// assert_eq!(parser.parse_peek("abc"), Ok(("bc", 'a'))); /// assert_eq!(parser.parse_peek(" abc"), Err(ErrMode::Backtrack(InputError::new(" abc", ErrorKind::Verify)))); /// assert_eq!(parser.parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Verify)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// ``` impl Parser::Token, E> for char where I: StreamIsPartial, I: Stream, ::Token: AsChar + Clone, E: ParserError, { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult<::Token, E> { crate::token::one_of(*self).parse_next(i) } } /// This is a shortcut for [`tag`][crate::token::tag]. /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::combinator::alt; /// # use winnow::token::take; /// /// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// alt((&"Hello"[..], take(5usize))).parse_next(s) /// } /// /// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..]))); /// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..]))); /// assert_eq!(parser.parse_peek(&b"Some"[..]), Err(ErrMode::Backtrack(InputError::new(&b"Some"[..], ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Slice)))); /// ``` impl<'s, I, E: ParserError> 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::tag(*self).parse_next(i) } } /// This is a shortcut for [`tag`][crate::token::tag]. /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::combinator::alt; /// # use winnow::token::take; /// /// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// alt((b"Hello", take(5usize))).parse_next(s) /// } /// /// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..]))); /// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..]))); /// assert_eq!(parser.parse_peek(&b"Some"[..]), Err(ErrMode::Backtrack(InputError::new(&b"Some"[..], ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Slice)))); /// ``` impl<'s, I, E: ParserError, 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::tag(*self).parse_next(i) } } /// This is a shortcut for [`tag`][crate::token::tag]. /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}}; /// # use winnow::combinator::alt; /// # use winnow::token::take; /// /// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// alt(("Hello", take(5usize))).parse_next(s) /// } /// /// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello"))); /// assert_eq!(parser.parse_peek("Something"), Ok(("hing", "Somet"))); /// assert_eq!(parser.parse_peek("Some"), Err(ErrMode::Backtrack(InputError::new("Some", ErrorKind::Slice)))); /// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` impl<'s, I, E: ParserError> 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::tag(*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),+); } } impl_parser_for_tuples!( P1 O1, P2 O2, P3 O3, P4 O4, P5 O5, P6 O6, P7 O7, P8 O8, P9 O9, P10 O10, P11 O11, P12 O12, P13 O13, P14 O14, P15 O15, P16 O16, P17 O17, P18 O18, P19 O19, P20 O20, P21 O21 ); #[cfg(feature = "alloc")] use alloc::boxed::Box; #[cfg(feature = "alloc")] impl<'a, I, O, E> Parser for Box + 'a> { #[inline(always)] fn parse_next(&mut self, i: &mut I) -> PResult { (**self).parse_next(i) } } /// Convert a [`Parser::parse_peek`] style parse function to be a [`Parser`] #[inline(always)] pub fn unpeek<'a, I, O, E>( mut peek: impl FnMut(I) -> IResult + '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::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.5.15/src/stream/impls.rs000064400000000000000000000321061046102023000150130ustar 00000000000000macro_rules! impl_partial_eq { ($lhs:ty, $rhs:ty) => { impl<'a, 'b> PartialEq<$rhs> for $lhs { #[inline] fn eq(&self, other: &$rhs) -> bool { let l = self.as_ref(); let r: &Self = other.as_ref(); PartialEq::eq(l, r) } } impl<'a, 'b> PartialEq<$lhs> for $rhs { #[inline] fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(other, self) } } }; } macro_rules! impl_partial_ord { ($lhs:ty, $rhs:ty) => { impl<'a, 'b> PartialOrd<$rhs> for $lhs { #[inline] fn partial_cmp(&self, other: &$rhs) -> Option { let l = self.as_ref(); let r: &Self = other.as_ref(); PartialOrd::partial_cmp(l, r) } } impl<'a, 'b> PartialOrd<$lhs> for $rhs { #[inline] fn partial_cmp(&self, other: &$lhs) -> Option { 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, "{:0>2x}", byte)?; } Ok(()) } } impl fmt::UpperHex for Bytes { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (i, byte) in self.as_bytes().iter().enumerate() { if 0 < i { let absolute = (self.as_bytes().as_ptr() as usize) + i; if f.alternate() && absolute != 0 && absolute % 4 == 0 { write!(f, "_")?; } } write!(f, "{:0>2X}", byte)?; } Ok(()) } } impl ops::Deref for Bytes { type Target = [u8]; #[inline] fn deref(&self) -> &[u8] { self.as_bytes() } } impl ops::Index 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 { PartialOrd::partial_cmp(self.as_bytes(), other.as_bytes()) } } impl Ord for Bytes { #[inline] fn cmp(&self, other: &Bytes) -> Ordering { self.partial_cmp(other).unwrap() } } impl_partial_ord!(Bytes, [u8]); impl_partial_ord!(Bytes, &'a [u8]); impl_partial_ord!(Bytes, str); impl_partial_ord!(Bytes, &'a str); #[cfg(all(test, feature = "std"))] mod display { use crate::stream::Bytes; #[test] fn clean() { assert_eq!(&format!("{}", Bytes::new(b"abc")), "616263"); assert_eq!(&format!("{}", Bytes::new(b"\xf0\x28\x8c\xbc")), "F0288CBC"); } } #[cfg(all(test, feature = "std"))] mod debug { use crate::stream::Bytes; #[test] fn test_debug() { assert_eq!( "000000206674797069736F6D0000020069736F6D69736F32617663316D70", format!( "{:?}", Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp") ), ); } #[test] fn test_pretty_debug() { // Output can change from run-to-run format!( "{:#?}", Bytes::new(b"\0\0\0 ftypisom\0\0\x02\0isomiso2avc1mp") ); } #[test] fn test_sliced() { // Output can change from run-to-run let total = Bytes::new(b"12345678901234567890"); format!("{:#?}", total); format!("{:#?}", &total[1..]); format!("{:#?}", &total[10..]); } } } mod bstr { use crate::lib::std::{cmp::Ordering, fmt, ops}; use crate::stream::BStr; #[cfg(feature = "alloc")] impl fmt::Display for BStr { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { crate::lib::std::string::String::from_utf8_lossy(self.as_bytes()).fmt(f) } } impl fmt::Debug for BStr { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if !f.alternate() { write!(f, "\"")?; } for byte in self.as_bytes() { let c = *byte as char; write!(f, "{}", c.escape_debug())?; } if !f.alternate() { write!(f, "\"")?; } Ok(()) } } impl ops::Deref for BStr { type Target = [u8]; #[inline] fn deref(&self) -> &[u8] { self.as_bytes() } } impl ops::Index 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 { PartialOrd::partial_cmp(self.as_bytes(), other.as_bytes()) } } impl Ord for BStr { #[inline] fn cmp(&self, other: &BStr) -> Ordering { self.partial_cmp(other).unwrap() } } 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.5.15/src/stream/mod.rs000064400000000000000000002053371046102023000144560ustar 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 //! - [`Located`] can track the location within the original buffer to report //! [spans][crate::Parser::with_span] //! - [`Stateful`] to thread global state through your parsers //! - [`Partial`] can mark an input as partial buffer that is being streamed into //! - [Custom stream types][crate::_topic::stream] use core::num::NonZeroUsize; use crate::error::Needed; use crate::lib::std::iter::{Cloned, Enumerate}; use crate::lib::std::slice::Iter; use crate::lib::std::str::from_utf8; use crate::lib::std::str::CharIndices; use crate::lib::std::str::FromStr; #[allow(unused_imports)] #[cfg(feature = "unstable-doc")] use crate::error::ErrMode; #[cfg(feature = "alloc")] use crate::lib::std::collections::BTreeMap; #[cfg(feature = "std")] use crate::lib::std::collections::HashMap; #[cfg(feature = "alloc")] use crate::lib::std::string::String; #[cfg(feature = "alloc")] use crate::lib::std::vec::Vec; mod impls; #[cfg(test)] mod tests; /// UTF-8 Stream pub type Str<'i> = &'i str; /// Improved `Debug` experience for `&[u8]` byte streams #[allow(clippy::derive_hash_xor_eq)] #[derive(Hash)] #[repr(transparent)] pub struct Bytes([u8]); impl Bytes { /// Make a stream out of a byte slice-like. #[inline] pub fn new>(bytes: &B) -> &Self { Self::from_bytes(bytes.as_ref()) } #[inline] fn from_bytes(slice: &[u8]) -> &Self { unsafe { crate::lib::std::mem::transmute(slice) } } #[inline] fn as_bytes(&self) -> &[u8] { &self.0 } } /// Improved `Debug` experience for `&[u8]` UTF-8-ish streams #[allow(clippy::derive_hash_xor_eq)] #[derive(Hash)] #[repr(transparent)] pub struct BStr([u8]); impl BStr { /// Make a stream out of a byte slice-like. #[inline] pub fn new>(bytes: &B) -> &Self { Self::from_bytes(bytes.as_ref()) } #[inline] fn from_bytes(slice: &[u8]) -> &Self { unsafe { crate::lib::std::mem::transmute(slice) } } #[inline] fn as_bytes(&self) -> &[u8] { &self.0 } } /// Allow collecting the span of a parsed token /// /// See [`Parser::span`][crate::Parser::span] and [`Parser::with_span`][crate::Parser::with_span] for more details #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Located { initial: I, input: I, } impl Located where I: Clone + Offset, { /// Wrap another Stream with span tracking pub fn new(input: I) -> Self { let initial = input.clone(); Self { initial, input } } fn location(&self) -> usize { self.input.offset_from(&self.initial) } } impl AsRef for Located { #[inline(always)] fn as_ref(&self) -> &I { &self.input } } impl crate::lib::std::ops::Deref for Located { type Target = I; #[inline(always)] fn deref(&self) -> &Self::Target { &self.input } } impl crate::lib::std::fmt::Display for Located { fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { self.input.fmt(f) } } /// Thread global state through your parsers /// /// Use cases /// - Recursion checks /// - Error recovery /// - Debugging /// /// # Example /// /// ``` /// # use std::cell::Cell; /// # use winnow::prelude::*; /// # use winnow::stream::Stateful; /// # use winnow::ascii::alpha1; /// # type Error = (); /// /// #[derive(Clone, Debug)] /// struct State<'s>(&'s Cell); /// /// impl<'s> State<'s> { /// fn count(&self) { /// self.0.set(self.0.get() + 1); /// } /// } /// /// type Stream<'is> = Stateful<&'is str, State<'is>>; /// /// fn word<'s>(i: &mut Stream<'s>) -> PResult<&'s str> { /// i.state.count(); /// alpha1.parse_next(i) /// } /// /// let data = "Hello"; /// let state = Cell::new(0); /// let input = Stream { input: data, state: State(&state) }; /// let output = word.parse(input).unwrap(); /// assert_eq!(state.get(), 1); /// ``` #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Stateful { /// 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) } } /// 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 recognizes 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 recognized characters, both parsers work the same way /// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd;")), Ok((Partial::new(";"), "abcd"))); /// assert_eq!(alpha0_complete.parse_peek("abcd;"), Ok((";", "abcd"))); /// /// // but when there's no limit, the partial version returns `Incomplete`, because it cannot /// // know if more input data should be recognized. The whole input could be "abcd;", or /// // "abcde;" /// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd")), Err(ErrMode::Incomplete(Needed::new(1)))); /// /// // while the complete version knows that all of the data is there /// assert_eq!(alpha0_complete.parse_peek("abcd"), Ok(("", "abcd"))); /// ``` #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Partial { input: I, partial: bool, } impl Partial where I: StreamIsPartial, { /// Create a partial input pub fn new(input: I) -> Self { debug_assert!( !I::is_partial_supported(), "`Partial` can only wrap complete sources" ); let partial = true; Self { input, partial } } /// Extract the original [`Stream`] #[inline(always)] pub fn into_inner(self) -> I { self.input } } impl Default for Partial where I: Default + StreamIsPartial, { 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) } } /// Abstract method to calculate the input length pub trait SliceLen { /// Calculates the input length, as indicated by its name, /// and the name of the trait itself fn slice_len(&self) -> usize; } impl<'a, T> SliceLen for &'a [T] { #[inline] fn slice_len(&self) -> usize { self.len() } } impl SliceLen for [T; LEN] { #[inline] fn slice_len(&self) -> usize { self.len() } } impl<'a, T, const LEN: usize> SliceLen for &'a [T; LEN] { #[inline] fn slice_len(&self) -> usize { self.len() } } impl<'a> SliceLen for &'a str { #[inline] fn slice_len(&self) -> usize { self.len() } } impl<'a> SliceLen for &'a Bytes { #[inline] fn slice_len(&self) -> usize { self.len() } } impl<'a> SliceLen for &'a BStr { #[inline] fn slice_len(&self) -> usize { self.len() } } impl 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 Located where I: SliceLen, { #[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 `Located<&[u8]>` or `&str` for `Located<&str>` type Slice: crate::lib::std::fmt::Debug; /// Iterate with the offset from the current location type IterOffsets: Iterator; /// 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 offaet 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) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint.0; } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { self } } impl<'i> Stream for &'i str { type Token = char; type Slice = &'i str; type IterOffsets = CharIndices<'i>; type Checkpoint = Checkpoint; #[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) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint.0; } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { self } } impl<'i> Stream for &'i Bytes { type Token = u8; type Slice = &'i [u8]; type IterOffsets = Enumerate>>; 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) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint.0; } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { self } } impl<'i> Stream for &'i BStr { type Token = u8; type Slice = &'i [u8]; type IterOffsets = Enumerate>>; 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) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { *self = checkpoint.0; } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { self } } impl 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)>; #[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.0.checkpoint(), self.1)) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { self.0.reset(checkpoint.0 .0); self.1 = checkpoint.0 .1; } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.0 } } /// Iterator for [bit][crate::binary::bits] stream (`(I, usize)`) pub struct BitOffsets { i: (I, 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 Located { 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.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { self.input.reset(checkpoint.0); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } impl 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.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { self.input.reset(checkpoint.0); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } impl 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.input.checkpoint()) } #[inline(always)] fn reset(&mut self, checkpoint: Self::Checkpoint) { self.input.reset(checkpoint.0); } #[inline(always)] fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { &self.input } } /// Number of indices input has advanced since start of parsing pub trait Location { /// Number of indices input has advanced since start of parsing fn location(&self) -> usize; } impl Location for Located where I: Clone + Offset, { #[inline(always)] fn location(&self) -> usize { self.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() } } /// Marks the input as being the complete buffer or a partial buffer for streaming input /// /// See [`Partial`] for marking a presumed complete buffer type as a streaming buffer. pub trait StreamIsPartial: Sized { /// Whether the stream is currently partial or complete type PartialState; /// Mark the stream is complete #[must_use] fn complete(&mut self) -> Self::PartialState; /// Restore the stream back to its previous state fn restore_partial(&mut self, state: Self::PartialState); /// Report whether the [`Stream`] is can ever be incomplete fn is_partial_supported() -> bool; /// Report whether the [`Stream`] is currently incomplete #[inline(always)] fn is_partial(&self) -> bool { Self::is_partial_supported() } } impl<'a, T> StreamIsPartial for &'a [T] { type PartialState = (); fn complete(&mut self) -> Self::PartialState {} fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl<'a> StreamIsPartial for &'a str { type PartialState = (); fn complete(&mut self) -> Self::PartialState { // Already complete } fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl<'a> StreamIsPartial for &'a Bytes { type PartialState = (); fn complete(&mut self) -> Self::PartialState { // Already complete } fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl<'a> StreamIsPartial for &'a BStr { type PartialState = (); fn complete(&mut self) -> Self::PartialState { // Already complete } fn restore_partial(&mut self, _state: Self::PartialState) {} #[inline(always)] fn is_partial_supported() -> bool { false } } impl StreamIsPartial for (I, usize) where I: StreamIsPartial, { type PartialState = I::PartialState; fn complete(&mut self) -> Self::PartialState { self.0.complete() } fn restore_partial(&mut self, state: Self::PartialState) { self.0.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.0.is_partial() } } impl StreamIsPartial for Located where I: StreamIsPartial, { type PartialState = I::PartialState; fn complete(&mut self) -> Self::PartialState { self.input.complete() } fn restore_partial(&mut self, state: Self::PartialState) { self.input.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.input.is_partial() } } impl StreamIsPartial for Stateful where I: StreamIsPartial, { type PartialState = I::PartialState; fn complete(&mut self) -> Self::PartialState { self.input.complete() } fn restore_partial(&mut self, state: Self::PartialState) { self.input.restore_partial(state); } #[inline(always)] fn is_partial_supported() -> bool { I::is_partial_supported() } #[inline(always)] fn is_partial(&self) -> bool { self.input.is_partial() } } impl StreamIsPartial for Partial where I: StreamIsPartial, { type PartialState = bool; fn complete(&mut self) -> Self::PartialState { core::mem::replace(&mut self.partial, false) } fn restore_partial(&mut self, state: Self::PartialState) { self.partial = state; } #[inline(always)] fn is_partial_supported() -> bool { true } #[inline(always)] fn is_partial(&self) -> bool { self.partial } } /// Useful functions to calculate the offset between slices and show a hexdump of a slice pub trait Offset { /// Offset between the first byte of `start` and the first byte of `self` fn offset_from(&self, start: &Start) -> usize; } impl<'a, T> Offset for &'a [T] { #[inline] fn offset_from(&self, start: &Self) -> usize { let fst = (*start).as_ptr(); let snd = (*self).as_ptr(); debug_assert!( fst <= snd, "`Offset::offset_to` only accepts slices of `self`" ); snd as usize - fst as usize } } impl<'a, T> Offset<<&'a [T] as Stream>::Checkpoint> for &'a [T] where T: Clone + crate::lib::std::fmt::Debug, { #[inline(always)] fn offset_from(&self, other: &<&'a [T] as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl<'a> Offset for &'a str { #[inline(always)] fn offset_from(&self, start: &Self) -> usize { self.as_bytes().offset_from(&start.as_bytes()) } } impl<'a> Offset<<&'a str as Stream>::Checkpoint> for &'a str { #[inline(always)] fn offset_from(&self, other: &<&'a str as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl<'a> Offset for &'a Bytes { #[inline(always)] fn offset_from(&self, start: &Self) -> usize { self.as_bytes().offset_from(&start.as_bytes()) } } impl<'a> Offset<<&'a Bytes as Stream>::Checkpoint> for &'a Bytes { #[inline(always)] fn offset_from(&self, other: &<&'a Bytes as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl<'a> Offset for &'a BStr { #[inline(always)] fn offset_from(&self, start: &Self) -> usize { self.as_bytes().offset_from(&start.as_bytes()) } } impl<'a> Offset<<&'a BStr as Stream>::Checkpoint> for &'a BStr { #[inline(always)] fn offset_from(&self, other: &<&'a BStr as Stream>::Checkpoint) -> usize { self.checkpoint().offset_from(other) } } impl 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 Located where I: Stream, { #[inline(always)] fn offset_from(&self, other: &Self) -> usize { self.offset_from(&other.checkpoint()) } } impl Offset< as Stream>::Checkpoint> for Located where I: Stream, { #[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: Clone + 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.0.offset_from(&start.0) } } /// Helper trait for types that can be viewed as a byte slice pub trait AsBytes { /// Casts the input type to a byte slice fn as_bytes(&self) -> &[u8]; } impl<'a> AsBytes for &'a [u8] { #[inline(always)] fn as_bytes(&self) -> &[u8] { self } } impl<'a> AsBytes for &'a Bytes { #[inline(always)] fn as_bytes(&self) -> &[u8] { (*self).as_bytes() } } impl AsBytes for Located where 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<'a> AsBStr for &'a [u8] { #[inline(always)] fn as_bstr(&self) -> &[u8] { self } } impl<'a> AsBStr for &'a BStr { #[inline(always)] fn as_bstr(&self) -> &[u8] { (*self).as_bytes() } } impl<'a> AsBStr for &'a str { #[inline(always)] fn as_bstr(&self) -> &[u8] { (*self).as_bytes() } } impl AsBStr for Located where 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 Ok, /// 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; /// Compares self to another value for equality /// independently of the case. /// /// Warning: for `&str`, the comparison is done /// by lowercasing both strings and comparing /// the result. This is a temporary solution until /// a better one appears fn compare_no_case(&self, t: T) -> CompareResult; } fn lowercase_byte(c: u8) -> u8 { match c { b'A'..=b'Z' => c - b'A' + b'a', _ => c, } } impl<'a, 'b> Compare<&'b [u8]> for &'a [u8] { #[inline] fn compare(&self, t: &'b [u8]) -> CompareResult { let pos = self.iter().zip(t.iter()).position(|(a, b)| a != b); match pos { Some(_) => CompareResult::Error, None => { if self.len() >= t.len() { CompareResult::Ok } else { CompareResult::Incomplete } } } } #[inline] fn compare_no_case(&self, t: &'b [u8]) -> CompareResult { if self .iter() .zip(t) .any(|(a, b)| lowercase_byte(*a) != lowercase_byte(*b)) { CompareResult::Error } else if self.len() < t.len() { CompareResult::Incomplete } else { CompareResult::Ok } } } impl<'a, const LEN: usize> Compare<[u8; LEN]> for &'a [u8] { #[inline(always)] fn compare(&self, t: [u8; LEN]) -> CompareResult { self.compare(&t[..]) } #[inline(always)] fn compare_no_case(&self, t: [u8; LEN]) -> CompareResult { self.compare_no_case(&t[..]) } } impl<'a, 'b, const LEN: usize> Compare<&'b [u8; LEN]> for &'a [u8] { #[inline(always)] fn compare(&self, t: &'b [u8; LEN]) -> CompareResult { self.compare(&t[..]) } #[inline(always)] fn compare_no_case(&self, t: &'b [u8; LEN]) -> CompareResult { self.compare_no_case(&t[..]) } } impl<'a, 'b> Compare<&'b str> for &'a [u8] { #[inline(always)] fn compare(&self, t: &'b str) -> CompareResult { self.compare(t.as_bytes()) } #[inline(always)] fn compare_no_case(&self, t: &'b str) -> CompareResult { self.compare_no_case(t.as_bytes()) } } impl<'a, 'b> Compare<&'b str> for &'a str { #[inline(always)] fn compare(&self, t: &'b str) -> CompareResult { self.as_bytes().compare(t.as_bytes()) } //FIXME: this version is too simple and does not use the current locale #[inline] fn compare_no_case(&self, t: &'b str) -> CompareResult { let pos = self .chars() .zip(t.chars()) .position(|(a, b)| a.to_lowercase().ne(b.to_lowercase())); match pos { Some(_) => CompareResult::Error, None => { if self.len() >= t.len() { CompareResult::Ok } else { CompareResult::Incomplete } } } } } impl<'a, 'b> Compare<&'b [u8]> for &'a str { #[inline(always)] fn compare(&self, t: &'b [u8]) -> CompareResult { AsBStr::as_bstr(self).compare(t) } #[inline(always)] fn compare_no_case(&self, t: &'b [u8]) -> CompareResult { AsBStr::as_bstr(self).compare_no_case(t) } } impl<'a, T> Compare for &'a Bytes where &'a [u8]: Compare, { #[inline(always)] fn compare(&self, t: T) -> CompareResult { let bytes = (*self).as_bytes(); bytes.compare(t) } #[inline(always)] fn compare_no_case(&self, t: T) -> CompareResult { let bytes = (*self).as_bytes(); bytes.compare_no_case(t) } } impl<'a, T> Compare for &'a BStr where &'a [u8]: Compare, { #[inline(always)] fn compare(&self, t: T) -> CompareResult { let bytes = (*self).as_bytes(); bytes.compare(t) } #[inline(always)] fn compare_no_case(&self, t: T) -> CompareResult { let bytes = (*self).as_bytes(); bytes.compare_no_case(t) } } impl Compare for Located where I: Compare, { #[inline(always)] fn compare(&self, other: U) -> CompareResult { self.input.compare(other) } #[inline(always)] fn compare_no_case(&self, other: U) -> CompareResult { self.input.compare_no_case(other) } } impl Compare for Stateful where I: Compare, { #[inline(always)] fn compare(&self, other: U) -> CompareResult { self.input.compare(other) } #[inline(always)] fn compare_no_case(&self, other: U) -> CompareResult { self.input.compare_no_case(other) } } impl Compare for Partial where I: Compare, { #[inline(always)] fn compare(&self, t: T) -> CompareResult { self.input.compare(t) } #[inline(always)] fn compare_no_case(&self, t: T) -> CompareResult { self.input.compare_no_case(t) } } /// Look for a slice in self pub trait FindSlice { /// Returns the offset of the slice if it is found fn find_slice(&self, substr: T) -> Option; } impl<'i, 's> FindSlice<&'s [u8]> for &'i [u8] { #[inline(always)] fn find_slice(&self, substr: &'s [u8]) -> Option { memmem(self, substr) } } impl<'i> FindSlice for &'i [u8] { #[inline(always)] fn find_slice(&self, substr: u8) -> Option { memchr(substr, self) } } impl<'i, 's> FindSlice<&'s str> for &'i [u8] { #[inline(always)] fn find_slice(&self, substr: &'s str) -> Option { self.find_slice(substr.as_bytes()) } } impl<'i, 's> FindSlice<&'s str> for &'i str { #[inline(always)] fn find_slice(&self, substr: &'s str) -> Option { self.find(substr) } } impl<'i> FindSlice for &'i str { #[inline(always)] fn find_slice(&self, substr: char) -> Option { self.find(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 Located where 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()` succeededThe /// /// The byte slice implementation will first convert it to a `&str`, then apply the `parse()` /// function fn parse_slice(&self) -> Option; } impl<'a, R: FromStr> ParseSlice for &'a [u8] { #[inline(always)] fn parse_slice(&self) -> Option { from_utf8(self).ok().and_then(|s| s.parse().ok()) } } impl<'a, R: FromStr> ParseSlice for &'a 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<'a, T> UpdateSlice for &'a [T] where T: Clone + crate::lib::std::fmt::Debug, { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { inner } } impl<'a> UpdateSlice for &'a str { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { inner } } impl<'a> UpdateSlice for &'a Bytes { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { Bytes::new(inner) } } impl<'a> UpdateSlice for &'a BStr { #[inline(always)] fn update_slice(self, inner: Self::Slice) -> Self { BStr::new(inner) } } impl UpdateSlice for Located where I: UpdateSlice, { #[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 privazte #[derive(Copy, Clone, Debug)] pub struct Checkpoint(T); /// A range bounded inclusively for counting parses performed #[derive(PartialEq, Eq)] pub struct Range { pub(crate) start_inclusive: usize, pub(crate) end_inclusive: Option, } 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, { #[inline(always)] fn initial(capacity: Option) -> Self { match capacity { Some(capacity) => HashMap::with_capacity(clamp_capacity::<(K, V)>(capacity)), None => HashMap::new(), } } #[inline(always)] fn accumulate(&mut self, (key, value): (K, V)) { self.insert(key, value); } } #[cfg(feature = "alloc")] #[inline] pub(crate) fn clamp_capacity(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 recognizes alphabetic /// characters outside of the 52 ASCII letters fn is_alpha(self) -> bool; /// Tests that self is an alphabetic character /// or a decimal digit fn is_alphanum(self) -> bool; /// Tests that self is a decimal digit fn is_dec_digit(self) -> bool; /// Tests that self is an hex digit fn is_hex_digit(self) -> bool; /// Tests that self is an octal digit fn is_oct_digit(self) -> bool; /// Gets the len in bytes for self fn len(self) -> usize; /// Tests that self is ASCII space or tab fn is_space(self) -> bool; /// Tests if byte is ASCII newline: \n fn is_newline(self) -> bool; } impl AsChar for u8 { #[inline(always)] fn as_char(self) -> char { self as char } #[inline] fn is_alpha(self) -> bool { matches!(self, 0x41..=0x5A | 0x61..=0x7A) } #[inline] fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() } #[inline] fn is_dec_digit(self) -> bool { matches!(self, 0x30..=0x39) } #[inline] fn is_hex_digit(self) -> bool { matches!(self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66) } #[inline] fn is_oct_digit(self) -> bool { matches!(self, 0x30..=0x37) } #[inline] fn len(self) -> usize { 1 } #[inline] fn is_space(self) -> bool { self == b' ' || self == b'\t' } #[inline] fn is_newline(self) -> bool { self == b'\n' } } impl<'a> AsChar for &'a u8 { #[inline(always)] fn as_char(self) -> char { *self as char } #[inline] fn is_alpha(self) -> bool { matches!(*self, 0x41..=0x5A | 0x61..=0x7A) } #[inline] fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() } #[inline] fn is_dec_digit(self) -> bool { matches!(*self, 0x30..=0x39) } #[inline] fn is_hex_digit(self) -> bool { matches!(*self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66) } #[inline] fn is_oct_digit(self) -> bool { matches!(*self, 0x30..=0x37) } #[inline] fn len(self) -> usize { 1 } #[inline] fn is_space(self) -> bool { *self == b' ' || *self == b'\t' } #[inline] fn is_newline(self) -> bool { *self == b'\n' } } impl AsChar for char { #[inline(always)] fn as_char(self) -> char { self } #[inline] fn is_alpha(self) -> bool { self.is_ascii_alphabetic() } #[inline] fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() } #[inline] fn is_dec_digit(self) -> bool { self.is_ascii_digit() } #[inline] fn is_hex_digit(self) -> bool { self.is_ascii_hexdigit() } #[inline] fn is_oct_digit(self) -> bool { self.is_digit(8) } #[inline] fn len(self) -> usize { self.len_utf8() } #[inline] fn is_space(self) -> bool { self == ' ' || self == '\t' } #[inline] fn is_newline(self) -> bool { self == '\n' } } impl<'a> AsChar for &'a char { #[inline(always)] fn as_char(self) -> char { *self } #[inline] fn is_alpha(self) -> bool { self.is_ascii_alphabetic() } #[inline] fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() } #[inline] fn is_dec_digit(self) -> bool { self.is_ascii_digit() } #[inline] fn is_hex_digit(self) -> bool { self.is_ascii_hexdigit() } #[inline] fn is_oct_digit(self) -> bool { self.is_digit(8) } #[inline] fn len(self) -> usize { self.len_utf8() } #[inline] fn is_space(self) -> bool { *self == ' ' || *self == '\t' } #[inline] fn is_newline(self) -> bool { *self == '\n' } } /// Check if a token in in a set of possible tokens /// /// This is generally implemented on patterns that a token may match and supports `u8` and `char` /// tokens along with the following patterns /// - `b'c'` and `'c'` /// - `b""` and `""` /// - `|c| true` /// - `b'a'..=b'z'`, `'a'..='z'` (etc for each [range type][std::ops]) /// - `(pattern1, pattern2, ...)` /// /// # Example /// /// For example, you could implement `hex_digit0` as: /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::token::take_while; /// fn hex_digit1<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> { /// take_while(1.., ('a'..='f', 'A'..='F', '0'..='9')).parse_next(input) /// } /// /// assert_eq!(hex_digit1.parse_peek("21cZ"), Ok(("Z", "21c"))); /// assert_eq!(hex_digit1.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice)))); /// assert_eq!(hex_digit1.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` pub trait ContainsToken { /// 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<'a> ContainsToken<&'a 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<'a> ContainsToken<&'a 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(not(feature = "simd"))] #[inline(always)] fn memchr(token: u8, slice: &[u8]) -> Option { slice.iter().position(|t| *t == token) } #[cfg(feature = "simd")] #[inline(always)] fn memmem(slice: &[u8], tag: &[u8]) -> Option { if tag.len() > slice.len() { return None; } let (&substr_first, substr_rest) = match tag.split_first() { Some(split) => split, // an empty substring is found at position 0 // This matches the behavior of str.find(""). None => return Some(0), }; if substr_rest.is_empty() { return memchr::memchr(substr_first, slice); } let mut offset = 0; let haystack = &slice[..slice.len() - substr_rest.len()]; while let Some(position) = memchr::memchr(substr_first, &haystack[offset..]) { offset += position; let next_offset = offset + 1; if &slice[next_offset..][..substr_rest.len()] == substr_rest { return Some(offset); } offset = next_offset; } None } #[cfg(not(feature = "simd"))] fn memmem(slice: &[u8], tag: &[u8]) -> Option { for i in 0..slice.len() { let subslice = &slice[i..]; if subslice.starts_with(tag) { return Some(i); } } None } winnow-0.5.15/src/stream/tests.rs000064400000000000000000000057561046102023000150440ustar 00000000000000#[cfg(feature = "std")] use proptest::prelude::*; use super::*; #[test] fn test_offset_u8() { let s = b"abcd123"; let a = &s[..]; let b = &a[2..]; let c = &a[..4]; let d = &a[3..5]; assert_eq!(b.offset_from(&a), 2); assert_eq!(c.offset_from(&a), 0); assert_eq!(d.offset_from(&a), 3); } #[test] fn test_offset_str() { let a = "abcřèÂßÇd123"; let b = &a[7..]; let c = &a[..5]; let d = &a[5..9]; assert_eq!(b.offset_from(&a), 7); assert_eq!(c.offset_from(&a), 0); assert_eq!(d.offset_from(&a), 5); } #[test] #[cfg(feature = "alloc")] fn test_bit_stream_empty() { let i = (&b""[..], 0); let actual = i.iter_offsets().collect::>(); 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"); } winnow-0.5.15/src/token/mod.rs000064400000000000000000001144611046102023000143000ustar 00000000000000//! Parsers extracting tokens from the stream #[cfg(test)] mod tests; use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::Needed; use crate::error::ParserError; use crate::lib::std::result::Result::Ok; use crate::stream::Range; use crate::stream::{Compare, CompareResult, ContainsToken, FindSlice, SliceLen, Stream}; use crate::stream::{StreamIsPartial, ToUsize}; use crate::trace::trace; use crate::PResult; use crate::Parser; /// Matches one token /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ```rust /// # use winnow::{token::any, error::ErrMode, error::{InputError, ErrorKind}}; /// # use winnow::prelude::*; /// fn parser(input: &str) -> IResult<&str, char> { /// any.parse_peek(input) /// } /// /// assert_eq!(parser("abc"), Ok(("bc",'a'))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// ``` /// /// ```rust /// # use winnow::{token::any, error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// assert_eq!(any::<_, InputError<_>>.parse_peek(Partial::new("abc")), Ok((Partial::new("bc"),'a'))); /// assert_eq!(any::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] #[doc(alias = "token")] pub fn any>(input: &mut I) -> PResult<::Token, E> where I: StreamIsPartial, I: Stream, { trace("any", move |input: &mut I| { if ::is_partial_supported() { any_::<_, _, true>(input) } else { any_::<_, _, true>(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 tag combinator's argument and will return the part of /// the input that matches the argument /// /// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern /// /// **Note:** [`Parser`][crate::Parser] is implemented for strings and byte strings as a convenience (complete /// only) /// /// # Example /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// use winnow::token::tag; /// /// fn parser(s: &str) -> IResult<&str, &str> { /// "Hello".parse_peek(s) /// } /// /// assert_eq!(parser("Hello, World!"), Ok((", World!", "Hello"))); /// assert_eq!(parser("Something"), Err(ErrMode::Backtrack(InputError::new("Something", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::Partial; /// use winnow::token::tag; /// /// fn parser(s: Partial<&str>) -> IResult, &str> { /// "Hello".parse_peek(s) /// } /// /// assert_eq!(parser(Partial::new("Hello, World!")), Ok((Partial::new(", World!"), "Hello"))); /// assert_eq!(parser(Partial::new("Something")), Err(ErrMode::Backtrack(InputError::new(Partial::new("Something"), ErrorKind::Tag)))); /// assert_eq!(parser(Partial::new("S")), Err(ErrMode::Backtrack(InputError::new(Partial::new("S"), ErrorKind::Tag)))); /// assert_eq!(parser(Partial::new("H")), Err(ErrMode::Incomplete(Needed::new(4)))); /// ``` #[inline(always)] #[doc(alias = "literal")] #[doc(alias = "bytes")] #[doc(alias = "just")] pub fn tag>(tag: T) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream + Compare, T: SliceLen + Clone, { trace("tag", move |i: &mut I| { let t = tag.clone(); if ::is_partial_supported() { tag_::<_, _, _, true>(i, t) } else { tag_::<_, _, _, false>(i, t) } }) } fn tag_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + Compare, T: SliceLen, { let tag_len = t.slice_len(); match i.compare(t) { CompareResult::Ok => Ok(i.next_slice(tag_len)), CompareResult::Incomplete if PARTIAL && i.is_partial() => { Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset()))) } CompareResult::Incomplete | CompareResult::Error => { let e: ErrorKind = ErrorKind::Tag; Err(ErrMode::from_error_kind(i, e)) } } } /// Recognizes a case insensitive literal. /// /// The input data will be compared to the tag combinator's argument and will return the part of /// the input that matches the argument with no regard to case. /// /// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::tag_no_case; /// /// fn parser(s: &str) -> IResult<&str, &str> { /// tag_no_case("hello").parse_peek(s) /// } /// /// assert_eq!(parser("Hello, World!"), Ok((", World!", "Hello"))); /// assert_eq!(parser("hello, World!"), Ok((", World!", "hello"))); /// assert_eq!(parser("HeLlO, World!"), Ok((", World!", "HeLlO"))); /// assert_eq!(parser("Something"), Err(ErrMode::Backtrack(InputError::new("Something", ErrorKind::Tag)))); /// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::token::tag_no_case; /// /// fn parser(s: Partial<&str>) -> IResult, &str> { /// tag_no_case("hello").parse_peek(s) /// } /// /// assert_eq!(parser(Partial::new("Hello, World!")), Ok((Partial::new(", World!"), "Hello"))); /// assert_eq!(parser(Partial::new("hello, World!")), Ok((Partial::new(", World!"), "hello"))); /// assert_eq!(parser(Partial::new("HeLlO, World!")), Ok((Partial::new(", World!"), "HeLlO"))); /// assert_eq!(parser(Partial::new("Something")), Err(ErrMode::Backtrack(InputError::new(Partial::new("Something"), ErrorKind::Tag)))); /// assert_eq!(parser(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(5)))); /// ``` #[inline(always)] #[doc(alias = "literal")] #[doc(alias = "bytes")] #[doc(alias = "just")] pub fn tag_no_case>( tag: T, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream + Compare, T: SliceLen + Clone, { trace("tag_no_case", move |i: &mut I| { let t = tag.clone(); if ::is_partial_supported() { tag_no_case_::<_, _, _, true>(i, t) } else { tag_no_case_::<_, _, _, false>(i, t) } }) } fn tag_no_case_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + Compare, T: SliceLen, { let tag_len = t.slice_len(); match i.compare_no_case(t) { CompareResult::Ok => Ok(i.next_slice(tag_len)), CompareResult::Incomplete if PARTIAL && i.is_partial() => { Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset()))) } CompareResult::Incomplete | CompareResult::Error => { let e: ErrorKind = ErrorKind::Tag; Err(ErrMode::from_error_kind(i, e)) } } } /// Recognize a token that matches the [pattern][ContainsToken] /// /// **Note:** [`Parser`][crate::Parser] is implemented as a convenience (complete /// only) for /// - `u8` /// - `char` /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::token::one_of; /// assert_eq!(one_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek("b"), Ok(("", 'b'))); /// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Verify)))); /// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// /// fn parser_fn(i: &str) -> IResult<&str, char> { /// one_of(|c| c == 'a' || c == 'b').parse_peek(i) /// } /// assert_eq!(parser_fn("abc"), Ok(("bc", 'a'))); /// assert_eq!(parser_fn("cd"), Err(ErrMode::Backtrack(InputError::new("cd", ErrorKind::Verify)))); /// assert_eq!(parser_fn(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// ``` /// /// ``` /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// # use winnow::token::one_of; /// assert_eq!(one_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek(Partial::new("b")), Ok((Partial::new(""), 'b'))); /// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("bc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("bc"), ErrorKind::Verify)))); /// assert_eq!(one_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// /// fn parser_fn(i: Partial<&str>) -> IResult, 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>(list: T) -> impl Parser::Token, Error> where I: StreamIsPartial, I: Stream, ::Token: Clone, T: ContainsToken<::Token>, { trace( "one_of", any.verify(move |t: &::Token| list.contains_token(t.clone())), ) } /// Recognize a token that does not match the [pattern][ContainsToken] /// /// *Complete version*: Will return an error if there's not enough input data. /// /// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError}; /// # use winnow::prelude::*; /// # use winnow::token::none_of; /// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek("z"), Ok(("", 'z'))); /// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b']).parse_peek("a"), Err(ErrMode::Backtrack(InputError::new("a", ErrorKind::Verify)))); /// assert_eq!(none_of::<_, _, InputError<_>>('a').parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token)))); /// ``` /// /// ``` /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// # use winnow::token::none_of; /// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek(Partial::new("z")), Ok((Partial::new(""), 'z'))); /// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b']).parse_peek(Partial::new("a")), Err(ErrMode::Backtrack(InputError::new(Partial::new("a"), ErrorKind::Verify)))); /// assert_eq!(none_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn none_of>(list: T) -> impl Parser::Token, Error> where I: StreamIsPartial, I: Stream, ::Token: Clone, T: ContainsToken<::Token>, { trace( "none_of", any.verify(move |t: &::Token| !list.contains_token(t.clone())), ) } /// Recognize the longest (m <= len <= n) input slice that matches the [pattern][ContainsToken] /// /// It will return an `ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice))` if the pattern wasn't met or is out /// of range (m <= len <= n). /// /// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the pattern reaches the end of the input or is too short. /// /// To recognize a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::recognize`][crate::Parser::recognize]. /// /// # Example /// /// Zero or more tokens: /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_while; /// use winnow::stream::AsChar; /// /// fn alpha(s: &[u8]) -> IResult<&[u8], &[u8]> { /// take_while(0.., AsChar::is_alpha).parse_peek(s) /// } /// /// assert_eq!(alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..]))); /// assert_eq!(alpha(b"12345"), Ok((&b"12345"[..], &b""[..]))); /// assert_eq!(alpha(b"latin"), Ok((&b""[..], &b"latin"[..]))); /// assert_eq!(alpha(b""), Ok((&b""[..], &b""[..]))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::token::take_while; /// use winnow::stream::AsChar; /// /// fn alpha(s: Partial<&[u8]>) -> IResult, &[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>( range: impl Into, list: T, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream, T: ContainsToken<::Token>, { let Range { start_inclusive, end_inclusive, } = range.into(); trace("take_while", move |i: &mut I| { match (start_inclusive, end_inclusive) { (0, None) => { if ::is_partial_supported() { take_while0_::<_, _, _, true>(i, &list) } else { take_while0_::<_, _, _, false>(i, &list) } } (1, None) => { if ::is_partial_supported() { take_while1_::<_, _, _, true>(i, &list) } else { take_while1_::<_, _, _, false>(i, &list) } } (start, end) => { let end = end.unwrap_or(usize::MAX); if ::is_partial_supported() { take_while_m_n_::<_, _, _, true>(i, start, end, &list) } else { take_while_m_n_::<_, _, _, false>(i, start, end, &list) } } } }) } fn take_while0_, const PARTIAL: bool>( input: &mut I, list: &T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, T: ContainsToken<::Token>, { if PARTIAL && input.is_partial() { take_till0_partial(input, |c| !list.contains_token(c)) } else { take_till0_complete(input, |c| !list.contains_token(c)) } } fn take_while1_, const PARTIAL: bool>( input: &mut I, list: &T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, T: ContainsToken<::Token>, { let e: ErrorKind = ErrorKind::Slice; if PARTIAL && input.is_partial() { take_till1_partial(input, |c| !list.contains_token(c), e) } else { take_till1_complete(input, |c| !list.contains_token(c), e) } } /// Looks for the first element of the input type for which the condition returns true, /// and returns the input up to this position. /// /// *Partial version*: If no element is found matching the condition, this will return `Incomplete` fn take_till0_partial>( input: &mut I, predicate: P, ) -> PResult<::Slice, E> where P: Fn(I::Token) -> bool, { let offset = input .offset_for(predicate) .ok_or_else(|| ErrMode::Incomplete(Needed::new(1)))?; Ok(input.next_slice(offset)) } /// Looks for the first element of the input type for which the condition returns true /// and returns the input up to this position. /// /// Fails if the produced slice is empty. /// /// *Partial version*: If no element is found matching the condition, this will return `Incomplete` fn take_till1_partial>( input: &mut I, predicate: P, e: ErrorKind, ) -> PResult<::Slice, E> where P: Fn(I::Token) -> bool, { let offset = input .offset_for(predicate) .ok_or_else(|| ErrMode::Incomplete(Needed::new(1)))?; if offset == 0 { Err(ErrMode::from_error_kind(input, e)) } else { Ok(input.next_slice(offset)) } } /// Looks for the first element of the input type for which the condition returns true, /// and returns the input up to this position. /// /// *Complete version*: If no element is found matching the condition, this will return the whole input fn take_till0_complete>( input: &mut I, predicate: P, ) -> PResult<::Slice, E> where P: Fn(I::Token) -> bool, { let offset = input .offset_for(predicate) .unwrap_or_else(|| input.eof_offset()); Ok(input.next_slice(offset)) } /// Looks for the first element of the input type for which the condition returns true /// and returns the input up to this position. /// /// Fails if the produced slice is empty. /// /// *Complete version*: If no element is found matching the condition, this will return the whole input fn take_till1_complete>( input: &mut I, predicate: P, e: ErrorKind, ) -> PResult<::Slice, E> where P: Fn(I::Token) -> bool, { let offset = input .offset_for(predicate) .unwrap_or_else(|| input.eof_offset()); if offset == 0 { Err(ErrMode::from_error_kind(input, e)) } else { Ok(input.next_slice(offset)) } } fn take_while_m_n_, const PARTIAL: bool>( input: &mut I, m: usize, n: usize, list: &T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream, T: ContainsToken<::Token>, { if n < m { return Err(ErrMode::assert(input, "`m` should be <= `n`")); } let mut final_count = 0; for (processed, (offset, token)) in input.iter_offsets().enumerate() { if !list.contains_token(token) { if processed < m { return Err(ErrMode::from_error_kind(input, ErrorKind::Slice)); } else { return Ok(input.next_slice(offset)); } } else { if processed == n { return Ok(input.next_slice(offset)); } final_count = processed + 1; } } if PARTIAL && input.is_partial() { if final_count == n { Ok(input.finish()) } else { let needed = if m > input.eof_offset() { m - input.eof_offset() } else { 1 }; Err(ErrMode::Incomplete(Needed::new(needed))) } } else { if m <= final_count { Ok(input.finish()) } else { Err(ErrMode::from_error_kind(input, ErrorKind::Slice)) } } } /// Recognize the longest input slice (if any) till a [pattern][ContainsToken] is met. /// /// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the /// end of input or if there was not match. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_till0; /// /// fn till_colon(s: &str) -> IResult<&str, &str> { /// take_till0(|c| c == ':').parse_peek(s) /// } /// /// assert_eq!(till_colon("latin:123"), Ok((":123", "latin"))); /// assert_eq!(till_colon(":empty matched"), Ok((":empty matched", ""))); //allowed /// assert_eq!(till_colon("12345"), Ok(("", "12345"))); /// assert_eq!(till_colon(""), Ok(("", ""))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::token::take_till0; /// /// fn till_colon(s: Partial<&str>) -> IResult, &str> { /// take_till0(|c| c == ':').parse_peek(s) /// } /// /// assert_eq!(till_colon(Partial::new("latin:123")), Ok((Partial::new(":123"), "latin"))); /// assert_eq!(till_colon(Partial::new(":empty matched")), Ok((Partial::new(":empty matched"), ""))); //allowed /// assert_eq!(till_colon(Partial::new("12345")), Err(ErrMode::Incomplete(Needed::new(1)))); /// assert_eq!(till_colon(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] pub fn take_till0>( list: T, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream, T: ContainsToken<::Token>, { trace("take_till0", move |i: &mut I| { if ::is_partial_supported() && i.is_partial() { take_till0_partial(i, |c| list.contains_token(c)) } else { take_till0_complete(i, |c| list.contains_token(c)) } }) } /// Recognize the longest (at least 1) input slice till a [pattern][ContainsToken] is met. /// /// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is empty or the /// predicate matches the first input. /// /// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the /// end of input or if there was not match. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_till1; /// /// fn till_colon(s: &str) -> IResult<&str, &str> { /// take_till1(|c| c == ':').parse_peek(s) /// } /// /// assert_eq!(till_colon("latin:123"), Ok((":123", "latin"))); /// assert_eq!(till_colon(":empty matched"), Err(ErrMode::Backtrack(InputError::new(":empty matched", ErrorKind::Slice)))); /// assert_eq!(till_colon("12345"), Ok(("", "12345"))); /// assert_eq!(till_colon(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// /// fn not_space(s: &str) -> IResult<&str, &str> { /// take_till1([' ', '\t', '\r', '\n']).parse_peek(s) /// } /// /// assert_eq!(not_space("Hello, World!"), Ok((" World!", "Hello,"))); /// assert_eq!(not_space("Sometimes\t"), Ok(("\t", "Sometimes"))); /// assert_eq!(not_space("Nospace"), Ok(("", "Nospace"))); /// assert_eq!(not_space(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::token::take_till1; /// /// fn till_colon(s: Partial<&str>) -> IResult, &str> { /// take_till1(|c| c == ':').parse_peek(s) /// } /// /// assert_eq!(till_colon(Partial::new("latin:123")), Ok((Partial::new(":123"), "latin"))); /// assert_eq!(till_colon(Partial::new(":empty matched")), Err(ErrMode::Backtrack(InputError::new(Partial::new(":empty matched"), ErrorKind::Slice)))); /// assert_eq!(till_colon(Partial::new("12345")), Err(ErrMode::Incomplete(Needed::new(1)))); /// assert_eq!(till_colon(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// /// fn not_space(s: Partial<&str>) -> IResult, &str> { /// take_till1([' ', '\t', '\r', '\n']).parse_peek(s) /// } /// /// assert_eq!(not_space(Partial::new("Hello, World!")), Ok((Partial::new(" World!"), "Hello,"))); /// assert_eq!(not_space(Partial::new("Sometimes\t")), Ok((Partial::new("\t"), "Sometimes"))); /// assert_eq!(not_space(Partial::new("Nospace")), Err(ErrMode::Incomplete(Needed::new(1)))); /// assert_eq!(not_space(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1)))); /// ``` #[inline(always)] #[doc(alias = "is_not")] pub fn take_till1>( list: T, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream, T: ContainsToken<::Token>, { trace("take_till1", move |i: &mut I| { let e: ErrorKind = ErrorKind::Slice; if ::is_partial_supported() && i.is_partial() { take_till1_partial(i, |c| list.contains_token(c), e) } else { take_till1_complete(i, |c| list.contains_token(c), e) } }) } /// Recognize an input slice containing the first N input elements (I[..N]). /// /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is shorter than the argument. /// /// *Partial version*: if the input has less than N elements, `take` will /// return a `ErrMode::Incomplete(Needed::new(M))` where M is the number of /// additional bytes the parser would need to succeed. /// It is well defined for `&[u8]` as the number of elements is the byte size, /// but for types like `&str`, we cannot know how many bytes correspond for /// the next few chars, so the result will be `ErrMode::Incomplete(Needed::Unknown)` /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take; /// /// fn take6(s: &str) -> IResult<&str, &str> { /// take(6usize).parse_peek(s) /// } /// /// assert_eq!(take6("1234567"), Ok(("7", "123456"))); /// assert_eq!(take6("things"), Ok(("", "things"))); /// assert_eq!(take6("short"), Err(ErrMode::Backtrack(InputError::new("short", ErrorKind::Slice)))); /// assert_eq!(take6(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// ``` /// /// The units that are taken will depend on the input type. For example, for a /// `&str` it will take a number of `char`'s, whereas for a `&[u8]` it will /// take that many `u8`'s: /// /// ```rust /// # use winnow::prelude::*; /// use winnow::error::InputError; /// use winnow::token::take; /// /// assert_eq!(take::<_, _, InputError<_>>(1usize).parse_peek("💙"), Ok(("", "💙"))); /// assert_eq!(take::<_, _, InputError<_>>(1usize).parse_peek("💙".as_bytes()), Ok((b"\x9F\x92\x99".as_ref(), b"\xF0".as_ref()))); /// ``` /// /// ```rust /// # use winnow::prelude::*; /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::Partial; /// use winnow::token::take; /// /// fn take6(s: Partial<&str>) -> IResult, &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>(count: C) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream, C: ToUsize, { let c = count.to_usize(); trace("take", move |i: &mut I| { 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 the literal. /// /// It doesn't consume the pattern. /// /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` /// if the pattern wasn't met. /// /// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't /// contain the pattern or if the input is smaller than the pattern. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_until0; /// /// fn until_eof(s: &str) -> IResult<&str, &str> { /// take_until0("eof").parse_peek(s) /// } /// /// assert_eq!(until_eof("hello, worldeof"), Ok(("eof", "hello, world"))); /// assert_eq!(until_eof("hello, world"), Err(ErrMode::Backtrack(InputError::new("hello, world", ErrorKind::Slice)))); /// assert_eq!(until_eof(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// assert_eq!(until_eof("1eof2eof"), Ok(("eof2eof", "1"))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::token::take_until0; /// /// fn until_eof(s: Partial<&str>) -> IResult, &str> { /// take_until0("eof").parse_peek(s) /// } /// /// assert_eq!(until_eof(Partial::new("hello, worldeof")), Ok((Partial::new("eof"), "hello, world"))); /// assert_eq!(until_eof(Partial::new("hello, world")), Err(ErrMode::Incomplete(Needed::Unknown))); /// assert_eq!(until_eof(Partial::new("hello, worldeo")), Err(ErrMode::Incomplete(Needed::Unknown))); /// assert_eq!(until_eof(Partial::new("1eof2eof")), Ok((Partial::new("eof2eof"), "1"))); /// ``` #[inline(always)] pub fn take_until0>( tag: T, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, T: SliceLen + Clone, { trace("take_until0", move |i: &mut I| { if ::is_partial_supported() { take_until0_::<_, _, _, true>(i, tag.clone()) } else { take_until0_::<_, _, _, false>(i, tag.clone()) } }) } fn take_until0_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, T: SliceLen, { match i.find_slice(t) { Some(offset) => Ok(i.next_slice(offset)), None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), } } /// Recognize the non empty input slice up to the first occurrence of the literal. /// /// It doesn't consume the pattern. /// /// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` /// if the pattern wasn't met. /// /// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't /// contain the pattern or if the input is smaller than the pattern. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// use winnow::token::take_until1; /// /// fn until_eof(s: &str) -> IResult<&str, &str> { /// take_until1("eof").parse_peek(s) /// } /// /// assert_eq!(until_eof("hello, worldeof"), Ok(("eof", "hello, world"))); /// assert_eq!(until_eof("hello, world"), Err(ErrMode::Backtrack(InputError::new("hello, world", ErrorKind::Slice)))); /// assert_eq!(until_eof(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice)))); /// assert_eq!(until_eof("1eof2eof"), Ok(("eof2eof", "1"))); /// assert_eq!(until_eof("eof"), Err(ErrMode::Backtrack(InputError::new("eof", ErrorKind::Slice)))); /// ``` /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::prelude::*; /// # use winnow::Partial; /// use winnow::token::take_until1; /// /// fn until_eof(s: Partial<&str>) -> IResult, &str> { /// take_until1("eof").parse_peek(s) /// } /// /// assert_eq!(until_eof(Partial::new("hello, worldeof")), Ok((Partial::new("eof"), "hello, world"))); /// assert_eq!(until_eof(Partial::new("hello, world")), Err(ErrMode::Incomplete(Needed::Unknown))); /// assert_eq!(until_eof(Partial::new("hello, worldeo")), Err(ErrMode::Incomplete(Needed::Unknown))); /// assert_eq!(until_eof(Partial::new("1eof2eof")), Ok((Partial::new("eof2eof"), "1"))); /// assert_eq!(until_eof(Partial::new("eof")), Err(ErrMode::Backtrack(InputError::new(Partial::new("eof"), ErrorKind::Slice)))); /// ``` #[inline(always)] pub fn take_until1>( tag: T, ) -> impl Parser::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, T: SliceLen + Clone, { trace("take_until1", move |i: &mut I| { if ::is_partial_supported() { take_until1_::<_, _, _, true>(i, tag.clone()) } else { take_until1_::<_, _, _, false>(i, tag.clone()) } }) } fn take_until1_, const PARTIAL: bool>( i: &mut I, t: T, ) -> PResult<::Slice, Error> where I: StreamIsPartial, I: Stream + FindSlice, T: SliceLen, { match i.find_slice(t) { None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)), None | Some(0) => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)), Some(offset) => Ok(i.next_slice(offset)), } } winnow-0.5.15/src/token/tests.rs000064400000000000000000000474531046102023000146710ustar 00000000000000use super::*; #[cfg(feature = "std")] use proptest::prelude::*; use crate::binary::length_data; use crate::combinator::delimited; use crate::error::ErrMode; use crate::error::ErrorKind; use crate::error::InputError; use crate::error::Needed; use crate::stream::AsChar; use crate::token::tag; use crate::unpeek; use crate::IResult; use crate::Parser; use crate::Partial; #[test] fn complete_take_while_m_n_utf8_all_matching() { let result: IResult<&str, &str> = take_while(1..=4, |c: char| c.is_alphabetic()).parse_peek("øn"); assert_eq!(result, Ok(("", "øn"))); } #[test] fn complete_take_while_m_n_utf8_all_matching_substring() { let result: IResult<&str, &str> = take_while(1, |c: char| c.is_alphabetic()).parse_peek("øn"); assert_eq!(result, Ok(("n", "ø"))); } #[cfg(feature = "std")] fn model_complete_take_while_m_n( m: usize, n: usize, valid: usize, input: &str, ) -> IResult<&str, &str> { if n < m { Err(crate::error::ErrMode::from_error_kind( &input, crate::error::ErrorKind::Slice, )) } else if m <= valid { let offset = n.min(valid); Ok((&input[offset..], &input[0..offset])) } else { Err(crate::error::ErrMode::from_error_kind( &input, crate::error::ErrorKind::Slice, )) } } #[cfg(feature = "std")] proptest! { #[test] #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253 fn complete_take_while_m_n_bounds(m in 0..20usize, n in 0..20usize, valid in 0..20usize, invalid in 0..20usize) { let input = format!("{:a>>.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, u8> { 'c'.parse_peek(i) } let a = &b"abcd"[..]; assert_eq!( f(Partial::new(a)), Err(ErrMode::Backtrack(error_position!( &Partial::new(a), ErrorKind::Verify ))) ); let b = &b"cde"[..]; assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), b'c'))); } #[test] fn char_str() { fn f(i: Partial<&str>) -> IResult, char> { 'c'.parse_peek(i) } let a = "abcd"; assert_eq!( f(Partial::new(a)), Err(ErrMode::Backtrack(error_position!( &Partial::new(a), ErrorKind::Verify ))) ); let b = "cde"; assert_eq!(f(Partial::new(b)), Ok((Partial::new("de"), 'c'))); } #[test] fn partial_none_of_test() { fn f(i: Partial<&[u8]>) -> IResult, 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_till1(['a', 'b']).parse_peek(i) } let a = Partial::new(&b"cdab"[..]); assert_eq!(a_or_b(a), Ok((Partial::new(&b"ab"[..]), &b"cd"[..]))); let b = Partial::new(&b"cbde"[..]); assert_eq!(a_or_b(b), Ok((Partial::new(&b"bde"[..]), &b"c"[..]))); let c = Partial::new(&b"abab"[..]); assert_eq!( a_or_b(c), Err(ErrMode::Backtrack(error_position!(&c, ErrorKind::Slice))) ); let d = Partial::new(&b"cdefba"[..]); assert_eq!(a_or_b(d), Ok((Partial::new(&b"ba"[..]), &b"cdef"[..]))); let e = Partial::new(&b"e"[..]); assert_eq!(a_or_b(e), Err(ErrMode::Incomplete(Needed::new(1)))); } #[test] fn partial_take_until_incomplete() { fn y(i: Partial<&[u8]>) -> IResult, &[u8]> { take_until0("end").parse_peek(i) } assert_eq!( y(Partial::new(&b"nd"[..])), Err(ErrMode::Incomplete(Needed::Unknown)) ); assert_eq!( y(Partial::new(&b"123"[..])), Err(ErrMode::Incomplete(Needed::Unknown)) ); assert_eq!( y(Partial::new(&b"123en"[..])), Err(ErrMode::Incomplete(Needed::Unknown)) ); } #[test] fn partial_take_until_incomplete_s() { fn ys(i: Partial<&str>) -> IResult, &str> { take_until0("end").parse_peek(i) } assert_eq!( ys(Partial::new("123en")), Err(ErrMode::Incomplete(Needed::Unknown)) ); } #[test] fn partial_recognize() { use crate::ascii::{ alpha1 as alpha, alphanumeric1 as alphanumeric, digit1 as digit, hex_digit1 as hex_digit, multispace1 as multispace, oct_digit1 as oct_digit, space1 as space, }; fn x(i: Partial<&[u8]>) -> IResult, &[u8]> { delimited("") .recognize() .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.recognize().parse_peek(i) } let ra = ya(Partial::new(&b"abc;"[..])); assert_eq!(ra, Ok((Partial::new(semicolon), &b"abc"[..]))); fn yd(i: Partial<&[u8]>) -> IResult, &[u8]> { digit.recognize().parse_peek(i) } let rd = yd(Partial::new(&b"123;"[..])); assert_eq!(rd, Ok((Partial::new(semicolon), &b"123"[..]))); fn yhd(i: Partial<&[u8]>) -> IResult, &[u8]> { hex_digit.recognize().parse_peek(i) } let rhd = yhd(Partial::new(&b"123abcDEF;"[..])); assert_eq!(rhd, Ok((Partial::new(semicolon), &b"123abcDEF"[..]))); fn yod(i: Partial<&[u8]>) -> IResult, &[u8]> { oct_digit.recognize().parse_peek(i) } let rod = yod(Partial::new(&b"1234567;"[..])); assert_eq!(rod, Ok((Partial::new(semicolon), &b"1234567"[..]))); fn yan(i: Partial<&[u8]>) -> IResult, &[u8]> { alphanumeric.recognize().parse_peek(i) } let ran = yan(Partial::new(&b"123abc;"[..])); assert_eq!(ran, Ok((Partial::new(semicolon), &b"123abc"[..]))); fn ys(i: Partial<&[u8]>) -> IResult, &[u8]> { space.recognize().parse_peek(i) } let rs = ys(Partial::new(&b" \t;"[..])); assert_eq!(rs, Ok((Partial::new(semicolon), &b" \t"[..]))); fn yms(i: Partial<&[u8]>) -> IResult, &[u8]> { multispace.recognize().parse_peek(i) } let rms = yms(Partial::new(&b" \t\r\n;"[..])); assert_eq!(rms, Ok((Partial::new(semicolon), &b" \t\r\n"[..]))); } #[test] fn partial_take_while0() { fn f(i: Partial<&[u8]>) -> IResult, &[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_till0(AsChar::is_alpha).parse_peek(i) } let a = &b""[..]; let b = &b"abcd"[..]; let c = &b"123abcd"[..]; let d = &b"123"[..]; assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1)))); assert_eq!( f(Partial::new(b)), Ok((Partial::new(&b"abcd"[..]), &b""[..])) ); assert_eq!( f(Partial::new(c)), Ok((Partial::new(&b"abcd"[..]), &b"123"[..])) ); assert_eq!(f(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1)))); } #[test] fn partial_take_till1() { fn f(i: Partial<&[u8]>) -> IResult, &[u8]> { take_till1(AsChar::is_alpha).parse_peek(i) } let a = &b""[..]; let b = &b"abcd"[..]; let c = &b"123abcd"[..]; let d = &b"123"[..]; assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1)))); assert_eq!( f(Partial::new(b)), Err(ErrMode::Backtrack(error_position!( &Partial::new(b), ErrorKind::Slice ))) ); assert_eq!( f(Partial::new(c)), Ok((Partial::new(&b"abcd"[..]), &b"123"[..])) ); assert_eq!(f(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1)))); } #[test] fn partial_take_while_utf8() { fn f(i: Partial<&str>) -> IResult, &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_till0(|c| c == '點').parse_peek(i) } assert_eq!( f(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( f(Partial::new("abcd")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!(f(Partial::new("abcd點")), Ok((Partial::new("點"), "abcd"))); assert_eq!( f(Partial::new("abcd點a")), Ok((Partial::new("點a"), "abcd")) ); fn g(i: Partial<&str>) -> IResult, &str> { take_till0(|c| c != '點').parse_peek(i) } assert_eq!( g(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點"))); assert_eq!( g(Partial::new("點點點a")), Ok((Partial::new("a"), "點點點")) ); } #[test] fn partial_take_utf8() { fn f(i: Partial<&str>) -> IResult, &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_recognize_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).recognize().parse_peek(i) } assert_eq!( x(Partial::new(&b"ab."[..])), Ok((Partial::new(&b"."[..]), &b"ab"[..])) ); assert_eq!( y(Partial::new(&b"ab."[..])), Ok((Partial::new(&b"."[..]), &b"ab"[..])) ); } #[test] fn partial_length_bytes() { use crate::binary::le_u8; fn x(i: Partial<&[u8]>) -> IResult, &[u8]> { length_data(le_u8).parse_peek(i) } assert_eq!( x(Partial::new(b"\x02..>>")), Ok((Partial::new(&b">>"[..]), &b".."[..])) ); assert_eq!( x(Partial::new(b"\x02..")), Ok((Partial::new(&[][..]), &b".."[..])) ); assert_eq!( x(Partial::new(b"\x02.")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( x(Partial::new(b"\x02")), Err(ErrMode::Incomplete(Needed::new(2))) ); fn y(i: Partial<&[u8]>) -> IResult, &[u8]> { let (i, _) = "magic".parse_peek(i)?; length_data(le_u8).parse_peek(i) } assert_eq!( y(Partial::new(b"magic\x02..>>")), Ok((Partial::new(&b">>"[..]), &b".."[..])) ); assert_eq!( y(Partial::new(b"magic\x02..")), Ok((Partial::new(&[][..]), &b".."[..])) ); assert_eq!( y(Partial::new(b"magic\x02.")), Err(ErrMode::Incomplete(Needed::new(1))) ); assert_eq!( y(Partial::new(b"magic\x02")), Err(ErrMode::Incomplete(Needed::new(2))) ); } #[cfg(feature = "alloc")] #[test] fn partial_case_insensitive() { fn test(i: Partial<&[u8]>) -> IResult, &[u8]> { tag_no_case("ABcd").parse_peek(i) } assert_eq!( test(Partial::new(&b"aBCdefgh"[..])), Ok((Partial::new(&b"efgh"[..]), &b"aBCd"[..])) ); assert_eq!( test(Partial::new(&b"abcdefgh"[..])), Ok((Partial::new(&b"efgh"[..]), &b"abcd"[..])) ); assert_eq!( test(Partial::new(&b"ABCDefgh"[..])), Ok((Partial::new(&b"efgh"[..]), &b"ABCD"[..])) ); assert_eq!( test(Partial::new(&b"ab"[..])), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( test(Partial::new(&b"Hello"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"Hello"[..]), ErrorKind::Tag ))) ); assert_eq!( test(Partial::new(&b"Hel"[..])), Err(ErrMode::Backtrack(error_position!( &Partial::new(&b"Hel"[..]), ErrorKind::Tag ))) ); fn test2(i: Partial<&str>) -> IResult, &str> { tag_no_case("ABcd").parse_peek(i) } assert_eq!( test2(Partial::new("aBCdefgh")), Ok((Partial::new("efgh"), "aBCd")) ); assert_eq!( test2(Partial::new("abcdefgh")), Ok((Partial::new("efgh"), "abcd")) ); assert_eq!( test2(Partial::new("ABCDefgh")), Ok((Partial::new("efgh"), "ABCD")) ); assert_eq!( test2(Partial::new("ab")), Err(ErrMode::Incomplete(Needed::new(2))) ); assert_eq!( test2(Partial::new("Hello")), Err(ErrMode::Backtrack(error_position!( &Partial::new("Hello"), ErrorKind::Tag ))) ); assert_eq!( test2(Partial::new("Hel")), Err(ErrMode::Backtrack(error_position!( &Partial::new("Hel"), ErrorKind::Tag ))) ); } #[test] fn partial_tag_fixed_size_array() { fn test(i: Partial<&[u8]>) -> IResult, &[u8]> { tag([0x42]).parse_peek(i) } fn test2(i: Partial<&[u8]>) -> IResult, &[u8]> { tag(&[0x42]).parse_peek(i) } let input = Partial::new(&[0x42, 0x00][..]); assert_eq!(test(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..]))); assert_eq!(test2(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..]))); } winnow-0.5.15/src/trace/internals.rs000064400000000000000000000200401046102023000154630ustar 00000000000000#![cfg(feature = "std")] use std::io::Write; use crate::error::ErrMode; use crate::stream::Stream; use crate::*; pub 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 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 struct Depth { depth: usize, inc: bool, } impl Depth { pub fn new() -> Self { let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst); let inc = true; Self { depth, inc } } pub fn existing() -> Self { let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst); let inc = false; Self { depth, inc } } } impl Drop for Depth { fn drop(&mut self) { if self.inc { let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst); } } } impl AsRef for Depth { #[inline(always)] fn as_ref(&self) -> &usize { &self.depth } } impl crate::lib::std::ops::Deref for Depth { type Target = usize; #[inline(always)] fn deref(&self) -> &Self::Target { &self.depth } } static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); pub enum Severity { Success, Backtrack, Cut, Incomplete, } impl Severity { pub fn with_result(result: &Result>) -> Self { match result { Ok(_) => Self::Success, Err(ErrMode::Backtrack(_)) => Self::Backtrack, Err(ErrMode::Cut(_)) => Self::Cut, Err(ErrMode::Incomplete(_)) => Self::Incomplete, } } } pub 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 fn end( depth: usize, name: &dyn crate::lib::std::fmt::Display, count: usize, consumed: usize, severity: Severity, ) { let gutter_style = anstyle::Style::new().bold(); let (call_width, _) = column_widths(); let count = if 0 < count { format!(":{count}") } else { "".to_owned() }; let call_column = format!("{:depth$}< {name}{count}", ""); let (status_style, status) = match severity { Severity::Success => { let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())); let status = format!("+{}", consumed); (style, status) } Severity::Backtrack => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())), "backtrack".to_owned(), ), Severity::Cut => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), "cut".to_owned(), ), Severity::Incomplete => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), "incomplete".to_owned(), ), }; let writer = anstream::stderr(); let mut writer = writer.lock(); let _ = writeln!( writer, "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}", gutter_style=gutter_style.render(), gutter_reset=gutter_style.render_reset(), status_style=status_style.render(), status_reset=status_style.render_reset(), ); } pub fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) { let gutter_style = anstyle::Style::new().bold(); let (call_width, _) = column_widths(); let call_column = format!("{:depth$}| {name}", ""); let (status_style, status) = match severity { Severity::Success => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())), "", ), Severity::Backtrack => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())), "backtrack", ), Severity::Cut => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), "cut", ), Severity::Incomplete => ( anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())), "incomplete", ), }; let writer = anstream::stderr(); let mut writer = writer.lock(); let _ = writeln!( writer, "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}", gutter_style=gutter_style.render(), gutter_reset=gutter_style.render_reset(), status_style=status_style.render(), status_reset=status_style.render_reset(), ); } fn column_widths() -> (usize, usize) { let term_width = term_width(); let min_call_width = 40; let min_input_width = 20; let decor_width = 3; let extra_width = term_width .checked_sub(min_call_width + min_input_width + decor_width) .unwrap_or_default(); let call_width = min_call_width + 2 * extra_width / 3; let input_width = min_input_width + extra_width / 3; (call_width, input_width) } fn term_width() -> usize { columns_env().or_else(query_width).unwrap_or(80) } fn query_width() -> Option { 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.5.15/src/trace/mod.rs000064400000000000000000000061051046102023000142510ustar 00000000000000//! Parser execution tracing //! //! By default, nothing happens and tracing gets compiled away as a no-op. To enable tracing, use //! `--features debug`. //! //! # Example //! //!![Trace output from string example](https://raw.githubusercontent.com/winnow-rs/winnow/main/assets/trace.svg "Example output") #[cfg(feature = "debug")] mod internals; use crate::error::ErrMode; use crate::stream::Stream; use crate::Parser; #[cfg(all(feature = "debug", not(feature = "std")))] compile_error!("`debug` requires `std`"); /// Trace the execution of the parser /// /// Note that [`Parser::context` also provides high level trace information. /// /// See [`trace` module][self] for more details. /// /// # Example /// /// ```rust /// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; /// # use winnow::token::take_while; /// # use winnow::stream::AsChar; /// # use winnow::prelude::*; /// use winnow::trace::trace; /// /// fn short_alpha<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> { /// trace("short_alpha", /// take_while(3..=6, AsChar::is_alpha) /// ).parse_next(s) /// } /// /// assert_eq!(short_alpha.parse_peek(b"latin123"), Ok((&b"123"[..], &b"latin"[..]))); /// assert_eq!(short_alpha.parse_peek(b"lengthy"), Ok((&b"y"[..], &b"length"[..]))); /// assert_eq!(short_alpha.parse_peek(b"latin"), Ok((&b""[..], &b"latin"[..]))); /// assert_eq!(short_alpha.parse_peek(b"ed"), Err(ErrMode::Backtrack(InputError::new(&b"ed"[..], ErrorKind::Slice)))); /// assert_eq!(short_alpha.parse_peek(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice)))); /// ``` #[cfg_attr(not(feature = "debug"), allow(unused_variables))] #[cfg_attr(not(feature = "debug"), allow(unused_mut))] #[cfg_attr(not(feature = "debug"), inline(always))] pub fn trace( 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); } } #[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()]); }