aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeongik Cha <jeongik@google.com>2023-09-27 10:19:05 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-09-27 10:19:05 +0000
commit62f09af3bc279fb72606da2c5ca852b0bcb0ac48 (patch)
tree350696077a5ebb249ba493e81609f1261c388bb0
parent3265c85493c3f05f3566738d24eb778db4872f91 (diff)
parent714fffa3369131131bb67fc4bc1b8edb02e9f8af (diff)
downloadwinnow-62f09af3bc279fb72606da2c5ca852b0bcb0ac48.tar.gz
Import winnow am: bd7107d898 am: c9f32604a8 am: 714fffa336
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/winnow/+/2752228 Change-Id: I886dd3007820bcf2613debcd8a72f4ea74510d90 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp18
-rw-r--r--Cargo.lock1415
-rw-r--r--Cargo.toml236
-rw-r--r--Cargo.toml.orig156
l---------LICENSE1
-rw-r--r--LICENSE-MIT18
-rw-r--r--METADATA19
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS1
-rw-r--r--README.md27
-rw-r--r--benches/contains_token.rs94
-rw-r--r--benches/number.rs70
-rw-r--r--examples/arithmetic/bench.rs21
-rw-r--r--examples/arithmetic/main.rs75
-rw-r--r--examples/arithmetic/parser.rs102
-rw-r--r--examples/arithmetic/parser_ast.rs168
-rw-r--r--examples/css/main.rs62
-rw-r--r--examples/css/parser.rs31
-rw-r--r--examples/custom_error.rs40
-rw-r--r--examples/http/bench.rs36
-rw-r--r--examples/http/main.rs47
-rw-r--r--examples/http/parser.rs138
-rw-r--r--examples/http/parser_streaming.rs139
-rw-r--r--examples/ini/bench.rs61
-rw-r--r--examples/ini/main.rs60
-rw-r--r--examples/ini/parser.rs146
-rw-r--r--examples/ini/parser_str.rs208
-rw-r--r--examples/iterator.rs77
-rw-r--r--examples/json/bench.rs70
-rw-r--r--examples/json/json.rs11
-rw-r--r--examples/json/main.rs98
-rw-r--r--examples/json/parser.rs322
-rw-r--r--examples/json/parser_dispatch.rs329
-rw-r--r--examples/json/parser_partial.rs348
-rw-r--r--examples/json_iterator.rs311
-rw-r--r--examples/ndjson/example.ndjson158
-rw-r--r--examples/ndjson/main.rs114
-rw-r--r--examples/ndjson/parser.rs338
-rw-r--r--examples/s_expression/main.rs20
-rw-r--r--examples/s_expression/parser.rs361
-rw-r--r--examples/string/main.rs70
-rw-r--r--examples/string/parser.rs165
-rw-r--r--src/_topic/arithmetic.rs13
-rw-r--r--src/_topic/error.rs13
-rw-r--r--src/_topic/fromstr.rs8
-rw-r--r--src/_topic/http.rs5
-rw-r--r--src/_topic/ini.rs5
-rw-r--r--src/_topic/json.rs5
-rw-r--r--src/_topic/language.rs330
-rw-r--r--src/_topic/mod.rs36
-rw-r--r--src/_topic/partial.rs46
-rw-r--r--src/_topic/performance.rs55
-rw-r--r--src/_topic/s_expression.rs5
-rw-r--r--src/_topic/stream.rs54
-rw-r--r--src/_topic/why.rs98
-rw-r--r--src/_tutorial/chapter_0.rs39
-rw-r--r--src/_tutorial/chapter_1.rs86
-rw-r--r--src/_tutorial/chapter_2.rs248
-rw-r--r--src/_tutorial/chapter_3.rs376
-rw-r--r--src/_tutorial/chapter_4.rs110
-rw-r--r--src/_tutorial/chapter_5.rs282
-rw-r--r--src/_tutorial/chapter_6.rs156
-rw-r--r--src/_tutorial/chapter_7.rs118
-rw-r--r--src/_tutorial/mod.rs12
-rw-r--r--src/ascii/mod.rs1675
-rw-r--r--src/ascii/tests.rs1554
-rw-r--r--src/binary/bits/mod.rs365
-rw-r--r--src/binary/bits/tests.rs191
-rw-r--r--src/binary/mod.rs2550
-rw-r--r--src/binary/tests.rs1270
-rw-r--r--src/combinator/branch.rs297
-rw-r--r--src/combinator/core.rs491
-rw-r--r--src/combinator/mod.rs172
-rw-r--r--src/combinator/multi.rs945
-rw-r--r--src/combinator/parser.rs863
-rw-r--r--src/combinator/sequence.rs164
-rw-r--r--src/combinator/tests.rs1292
-rw-r--r--src/error.rs1382
-rw-r--r--src/lib.rs249
-rw-r--r--src/macros.rs78
-rw-r--r--src/parser.rs977
-rw-r--r--src/stream/impls.rs537
-rw-r--r--src/stream/mod.rs2727
-rw-r--r--src/stream/tests.rs116
-rw-r--r--src/token/mod.rs1062
-rw-r--r--src/token/tests.rs701
-rw-r--r--src/trace/internals.rs244
-rw-r--r--src/trace/mod.rs112
89 files changed, 28301 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..57ad92d
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "e01b1a91a876616fcbcf0f1caff25523898cba2a"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..ca2f720
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,18 @@
+// This file is generated by cargo2android.py --run.
+// Do not modify this file as changes will be overridden on upgrade.
+
+
+
+rust_library_host {
+ name: "libwinnow",
+ crate_name: "winnow",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.5.14",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ features: [
+ "alloc",
+ "default",
+ "std",
+ ],
+}
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..43720ad
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,1415 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
+name = "anstream"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi 0.1.19",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "bytecount"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "ciborium"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
+[[package]]
+name = "circular"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea"
+
+[[package]]
+name = "clap"
+version = "4.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
+dependencies = [
+ "anstyle",
+ "clap_lex",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "criterion"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
+dependencies = [
+ "anes",
+ "cast",
+ "ciborium",
+ "clap",
+ "criterion-plot",
+ "is-terminal",
+ "itertools",
+ "num-traits",
+ "once_cell",
+ "oorandom",
+ "plotters",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "tinytemplate",
+ "walkdir",
+]
+
+[[package]]
+name = "criterion-plot"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
+dependencies = [
+ "cast",
+ "itertools",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "diff"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+
+[[package]]
+name = "digest"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "doc-comment"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "escargot"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6"
+dependencies = [
+ "log",
+ "once_cell",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "half"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
+
+[[package]]
+name = "handlebars"
+version = "4.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a"
+dependencies = [
+ "log",
+ "pest",
+ "pest_derive",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "rustix 0.38.6",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lexopt"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "libm"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "normalize-line-endings"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi 0.2.6",
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "oorandom"
+version = "11.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+
+[[package]]
+name = "os_pipe"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12"
+dependencies = [
+ "libc",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "output_vt100"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "pest"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660"
+dependencies = [
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha2",
+]
+
+[[package]]
+name = "plotters"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
+dependencies = [
+ "num-traits",
+ "plotters-backend",
+ "plotters-svg",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "plotters-backend"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "pretty_assertions"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
+dependencies = [
+ "ctor",
+ "diff",
+ "output_vt100",
+ "yansi",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proptest"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65"
+dependencies = [
+ "bit-set",
+ "bitflags 1.3.2",
+ "byteorder",
+ "lazy_static",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax 0.6.29",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quick-xml"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
+dependencies = [
+ "regex-syntax 0.7.2",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+
+[[package]]
+name = "rustix"
+version = "0.37.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
+dependencies = [
+ "bitflags 1.3.2",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.3.8",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f"
+dependencies = [
+ "bitflags 2.3.3",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.5",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.164"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.164"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.22",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "similar"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
+
+[[package]]
+name = "snapbox"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "escargot",
+ "normalize-line-endings",
+ "similar",
+ "snapbox-macros",
+]
+
+[[package]]
+name = "snapbox-macros"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31"
+dependencies = [
+ "anstream",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix 0.37.20",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "term-transcript"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d77280c9041d978e4ffedbdd19f710a1360085510c5e99dbcfec8e2f2bd4285"
+dependencies = [
+ "atty",
+ "bytecount",
+ "handlebars",
+ "os_pipe",
+ "pretty_assertions",
+ "quick-xml",
+ "serde",
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+dependencies = [
+ "rustix 0.37.20",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.22",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.22",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.1",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "winnow"
+version = "0.5.14"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "circular",
+ "criterion",
+ "doc-comment",
+ "escargot",
+ "is-terminal",
+ "lexopt",
+ "memchr",
+ "proptest",
+ "snapbox",
+ "term-transcript",
+ "terminal_size",
+]
+
+[[package]]
+name = "yansi"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..522276c
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,236 @@
+# 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.14"
+include = [
+ "build.rs",
+ "src/**/*",
+ "Cargo.toml",
+ "Cargo.lock",
+ "LICENSE*",
+ "README.md",
+ "benches/**/*",
+ "examples/**/*",
+]
+autoexamples = false
+description = "A byte-oriented, zero-copy, parser combinators library"
+readme = "README.md"
+keywords = [
+ "parser",
+ "parser-combinators",
+ "parsing",
+ "streaming",
+ "bit",
+]
+categories = ["parsing"]
+license = "MIT"
+repository = "https://github.com/winnow-rs/winnow"
+
+[package.metadata.docs.rs]
+cargo-args = [
+ "-Zunstable-options",
+ "-Zrustdoc-scrape-examples",
+]
+features = ["unstable-doc"]
+rustdoc-args = [
+ "--cfg",
+ "docsrs",
+]
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+min = 1
+replace = "{{version}}"
+search = "Unreleased"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = "...{{tag_name}}"
+search = '\.\.\.HEAD'
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+min = 1
+replace = "{{date}}"
+search = "ReleaseDate"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = """
+<!-- next-header -->
+## [Unreleased] - ReleaseDate
+"""
+search = "<!-- next-header -->"
+
+[[package.metadata.release.pre-release-replacements]]
+exactly = 1
+file = "CHANGELOG.md"
+replace = """
+<!-- next-url -->
+[Unreleased]: https://github.com/winnow-rs/winnow/compare/{{tag_name}}...HEAD"""
+search = "<!-- next-url -->"
+
+[profile.bench]
+lto = true
+codegen-units = 1
+debug = 2
+
+[[example]]
+name = "arithmetic"
+test = true
+required-features = ["alloc"]
+
+[[example]]
+name = "css"
+test = true
+required-features = ["alloc"]
+
+[[example]]
+name = "custom_error"
+test = true
+required-features = ["alloc"]
+
+[[example]]
+name = "http"
+required-features = ["alloc"]
+
+[[example]]
+name = "ini"
+test = true
+required-features = ["std"]
+
+[[example]]
+name = "json"
+test = true
+required-features = ["std"]
+
+[[example]]
+name = "ndjson"
+test = true
+required-features = ["std"]
+
+[[example]]
+name = "json_iterator"
+required-features = ["std"]
+
+[[example]]
+name = "iterator"
+
+[[example]]
+name = "s_expression"
+required-features = ["alloc"]
+
+[[example]]
+name = "string"
+required-features = ["alloc"]
+
+[[bench]]
+name = "arithmetic"
+path = "examples/arithmetic/bench.rs"
+harness = false
+
+[[bench]]
+name = "contains_token"
+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",
+]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..3e7b219
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,156 @@
+[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.14"
+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="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1},
+ {file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\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 = "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"]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..7f9a88e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-MIT \ No newline at end of file
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..c9b44cb
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,18 @@
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..09f58bb
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+name: "winnow"
+description: "A byte-oriented, zero-copy, parser combinators library"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "https://crates.io/crates/winnow"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/winnow/winnow-0.5.14.crate"
+ }
+ version: "0.5.14"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2023
+ month: 8
+ day: 23
+ }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..45dc4dd
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:master:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..507df4e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# winnow, making parsing a breeze
+
+[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
+[![Build Status](https://github.com/winnow-rs/winnow/actions/workflows/ci.yml/badge.svg)](https://github.com/winnow-rs/winnow/actions/workflows/ci.yml)
+[![Coverage Status](https://coveralls.io/repos/github/winnow-rs/winnow/badge.svg?branch=main)](https://coveralls.io/github/winnow-rs/winnow?branch=main)
+[![Crates.io Version](https://img.shields.io/crates/v/winnow.svg)](https://crates.io/crates/winnow)
+
+## About
+
+Build up a parser for your format of choice with the building blocks provided by `winnow`.
+
+For more details, see:
+- [Tutorial](https://docs.rs/winnow/latest/winnow/_tutorial/index.html)
+- [Special Topics](https://docs.rs/winnow/latest/winnow/_topic/index.html)
+ - [Why winnow? How does it compare to ...?](https://docs.rs/winnow/latest/winnow/_topic/why/index.html)
+- [API Reference](https://docs.rs/winnow)
+- [List of combinators](https://docs.rs/winnow/latest/winnow/combinator/index.html)
+
+# Contributors
+
+winnow is the fruit of the work of many contributors over the years, many
+thanks for your help! In particular, thanks to [Geal](https://github.com/Geal)
+for the original [`nom` crate](https://crates.io/crates/nom).
+
+<a href="https://github.com/winnow-rs/winnow/graphs/contributors">
+ <img src="https://contributors-img.web.app/image?repo=winnow-rs/winnow" />
+</a>
diff --git a/benches/contains_token.rs b/benches/contains_token.rs
new file mode 100644
index 0000000..2980ce6
--- /dev/null
+++ b/benches/contains_token.rs
@@ -0,0 +1,94 @@
+use criterion::black_box;
+
+use winnow::combinator::alt;
+use winnow::combinator::repeat;
+use winnow::prelude::*;
+use winnow::token::take_till1;
+use winnow::token::take_while;
+
+fn contains_token(c: &mut criterion::Criterion) {
+ let data = [
+ ("contiguous", CONTIGUOUS),
+ ("interleaved", INTERLEAVED),
+ ("canada", CANADA),
+ ];
+ let mut group = c.benchmark_group("contains_token");
+ for (name, sample) in data {
+ let len = sample.len();
+ group.throughput(criterion::Throughput::Bytes(len as u64));
+
+ group.bench_with_input(criterion::BenchmarkId::new("slice", name), &len, |b, _| {
+ b.iter(|| black_box(parser_slice.parse_peek(black_box(sample)).unwrap()));
+ });
+ group.bench_with_input(criterion::BenchmarkId::new("array", name), &len, |b, _| {
+ b.iter(|| black_box(parser_array.parse_peek(black_box(sample)).unwrap()));
+ });
+ group.bench_with_input(criterion::BenchmarkId::new("tuple", name), &len, |b, _| {
+ b.iter(|| black_box(parser_tuple.parse_peek(black_box(sample)).unwrap()));
+ });
+ group.bench_with_input(
+ criterion::BenchmarkId::new("closure-or", name),
+ &len,
+ |b, _| {
+ b.iter(|| black_box(parser_closure_or.parse_peek(black_box(sample)).unwrap()));
+ },
+ );
+ group.bench_with_input(
+ criterion::BenchmarkId::new("closure-matches", name),
+ &len,
+ |b, _| {
+ b.iter(|| {
+ black_box(
+ parser_closure_matches
+ .parse_peek(black_box(sample))
+ .unwrap(),
+ )
+ });
+ },
+ );
+ }
+ group.finish();
+}
+
+fn parser_slice(input: &mut &str) -> PResult<usize> {
+ let contains = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'][..];
+ repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
+}
+
+fn parser_array(input: &mut &str) -> PResult<usize> {
+ let contains = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
+ repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
+}
+
+fn parser_tuple(input: &mut &str) -> PResult<usize> {
+ let contains = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
+ repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
+}
+
+fn parser_closure_or(input: &mut &str) -> PResult<usize> {
+ let contains = |c: char| {
+ c == '0'
+ || c == '1'
+ || c == '2'
+ || c == '3'
+ || c == '4'
+ || c == '5'
+ || c == '6'
+ || c == '7'
+ || c == '8'
+ || c == '9'
+ };
+ repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
+}
+
+fn parser_closure_matches(input: &mut &str) -> PResult<usize> {
+ let contains = |c: char| matches!(c, '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9');
+ repeat(0.., alt((take_while(1.., contains), take_till1(contains)))).parse_next(input)
+}
+
+const CONTIGUOUS: &str = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+const INTERLEAVED: &str = "0123456789abc0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab0123456789ab";
+const CANADA: &str = include_str!("../third_party/nativejson-benchmark/data/canada.json");
+
+criterion::criterion_group!(benches, contains_token);
+criterion::criterion_main!(benches);
diff --git a/benches/number.rs b/benches/number.rs
new file mode 100644
index 0000000..d35d65c
--- /dev/null
+++ b/benches/number.rs
@@ -0,0 +1,70 @@
+#[macro_use]
+extern crate criterion;
+
+use criterion::Criterion;
+
+use winnow::ascii::float;
+use winnow::binary::be_u64;
+use winnow::error::ErrMode;
+use winnow::error::ErrorKind;
+use winnow::error::InputError;
+use winnow::error::ParserError;
+use winnow::prelude::*;
+use winnow::stream::ParseSlice;
+
+type Stream<'i> = &'i [u8];
+
+fn parser(i: &mut Stream<'_>) -> PResult<u64> {
+ be_u64.parse_next(i)
+}
+
+fn number(c: &mut Criterion) {
+ let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+
+ parser
+ .parse_peek(&data[..])
+ .expect("should parse correctly");
+ c.bench_function("number", move |b| {
+ b.iter(|| parser.parse_peek(&data[..]).unwrap());
+ });
+}
+
+fn float_bytes(c: &mut Criterion) {
+ println!(
+ "float_bytes result: {:?}",
+ float::<_, f64, InputError<_>>.parse_peek(&b"-1.234E-12"[..])
+ );
+ c.bench_function("float bytes", |b| {
+ b.iter(|| float::<_, f64, InputError<_>>.parse_peek(&b"-1.234E-12"[..]));
+ });
+}
+
+fn float_str(c: &mut Criterion) {
+ println!(
+ "float_str result: {:?}",
+ float::<_, f64, InputError<_>>.parse_peek("-1.234E-12")
+ );
+ c.bench_function("float str", |b| {
+ b.iter(|| float::<_, f64, InputError<_>>.parse_peek("-1.234E-12"));
+ });
+}
+
+fn std_float(input: &mut &[u8]) -> PResult<f64> {
+ match input.parse_slice() {
+ Some(n) => Ok(n),
+ None => Err(ErrMode::from_error_kind(input, ErrorKind::Slice)),
+ }
+}
+
+fn std_float_bytes(c: &mut Criterion) {
+ println!(
+ "std_float_bytes result: {:?}",
+ std_float.parse_peek(&b"-1.234E-12"[..])
+ );
+ c.bench_function("std_float bytes", |b| {
+ b.iter(|| std_float.parse_peek(&b"-1.234E-12"[..]));
+ });
+}
+
+criterion_group!(benches, number, float_bytes, std_float_bytes, float_str);
+criterion_main!(benches);
diff --git a/examples/arithmetic/bench.rs b/examples/arithmetic/bench.rs
new file mode 100644
index 0000000..6504454
--- /dev/null
+++ b/examples/arithmetic/bench.rs
@@ -0,0 +1,21 @@
+mod 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);
diff --git a/examples/arithmetic/main.rs b/examples/arithmetic/main.rs
new file mode 100644
index 0000000..94a17d8
--- /dev/null
+++ b/examples/arithmetic/main.rs
@@ -0,0 +1,75 @@
+use 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<String>,
+ implementation: Impl,
+}
+
+enum Impl {
+ Eval,
+ Ast,
+}
+
+impl Default for Impl {
+ fn default() -> Self {
+ Self::Eval
+ }
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Long("impl") => {
+ res.implementation = args.value()?.parse_with(|s| match s {
+ "eval" => Ok(Impl::Eval),
+ "ast" => Ok(Impl::Ast),
+ _ => Err("expected `eval`, `ast`"),
+ })?;
+ }
+ Value(input) => {
+ res.input = Some(input.string()?);
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
diff --git a/examples/arithmetic/parser.rs b/examples/arithmetic/parser.rs
new file mode 100644
index 0000000..50ffbdb
--- /dev/null
+++ b/examples/arithmetic/parser.rs
@@ -0,0 +1,102 @@
+use 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<i64> {
+ let init = term.parse_next(i)?;
+
+ fold_repeat(
+ 0..,
+ (one_of(['+', '-']), term),
+ move || init,
+ |acc, (op, val): (char, i64)| {
+ if op == '+' {
+ acc + val
+ } else {
+ acc - val
+ }
+ },
+ )
+ .parse_next(i)
+}
+
+// We read an initial factor and for each time we find
+// a * or / operator followed by another factor, we do
+// the math by folding everything
+fn term(i: &mut &str) -> PResult<i64> {
+ let init = factor.parse_next(i)?;
+
+ fold_repeat(
+ 0..,
+ (one_of(['*', '/']), factor),
+ move || init,
+ |acc, (op, val): (char, i64)| {
+ if op == '*' {
+ acc * val
+ } else {
+ acc / val
+ }
+ },
+ )
+ .parse_next(i)
+}
+
+// We transform an integer string into a i64, ignoring surrounding whitespaces
+// We look for a digit suite, and try to convert it.
+// If either str::from_utf8 or FromStr::from_str fail,
+// we fallback to the parens parser defined above
+fn factor(i: &mut &str) -> PResult<i64> {
+ delimited(
+ 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<i64> {
+ 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)));
+}
diff --git a/examples/arithmetic/parser_ast.rs b/examples/arithmetic/parser_ast.rs
new file mode 100644
index 0000000..5fb9847
--- /dev/null
+++ b/examples/arithmetic/parser_ast.rs
@@ -0,0 +1,168 @@
+use 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<Expr>, Box<Expr>),
+ Sub(Box<Expr>, Box<Expr>),
+ Mul(Box<Expr>, Box<Expr>),
+ Div(Box<Expr>, Box<Expr>),
+ Paren(Box<Expr>),
+}
+
+#[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<Expr> {
+ 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<Expr> {
+ 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<Expr> {
+ alt((
+ delimited(multispace, digit, multispace)
+ .try_map(FromStr::from_str)
+ .map(Expr::Value),
+ parens,
+ ))
+ .parse_next(i)
+}
+
+fn parens(i: &mut &str) -> PResult<Expr> {
+ 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))")
+ ))
+ );
+}
diff --git a/examples/css/main.rs b/examples/css/main.rs
new file mode 100644
index 0000000..cf52ada
--- /dev/null
+++ b/examples/css/main.rs
@@ -0,0 +1,62 @@
+use winnow::prelude::*;
+
+mod parser;
+
+use parser::hex_color;
+
+fn main() -> Result<(), lexopt::Error> {
+ let args = Args::parse()?;
+
+ let input = args.input.as_deref().unwrap_or("#AAAAAA");
+
+ println!("{} =", input);
+ match hex_color.parse(input) {
+ Ok(result) => {
+ println!(" {:?}", result);
+ }
+ Err(err) => {
+ println!(" {}", err);
+ }
+ }
+
+ Ok(())
+}
+
+#[derive(Default)]
+struct Args {
+ input: Option<String>,
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Value(input) => {
+ res.input = Some(input.string()?);
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
+
+#[test]
+fn parse_color() {
+ assert_eq!(
+ hex_color.parse_peek("#2F14DF"),
+ Ok((
+ "",
+ parser::Color {
+ red: 47,
+ green: 20,
+ blue: 223,
+ }
+ ))
+ );
+}
diff --git a/examples/css/parser.rs b/examples/css/parser.rs
new file mode 100644
index 0000000..d31ed0b
--- /dev/null
+++ b/examples/css/parser.rs
@@ -0,0 +1,31 @@
+use winnow::prelude::*;
+use winnow::token::take_while;
+
+#[derive(Debug, Eq, PartialEq)]
+pub struct Color {
+ pub red: u8,
+ pub green: u8,
+ pub blue: u8,
+}
+
+impl std::str::FromStr for Color {
+ // The error must be owned
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ hex_color.parse(s).map_err(|e| e.to_string())
+ }
+}
+
+pub fn hex_color(input: &mut &str) -> PResult<Color> {
+ let _ = "#".parse_next(input)?;
+ let (red, green, blue) = (hex_primary, hex_primary, hex_primary).parse_next(input)?;
+
+ Ok(Color { red, green, blue })
+}
+
+fn hex_primary(input: &mut &str) -> PResult<u8> {
+ take_while(2, |c: char| c.is_ascii_hexdigit())
+ .try_map(|input| u8::from_str_radix(input, 16))
+ .parse_next(input)
+}
diff --git a/examples/custom_error.rs b/examples/custom_error.rs
new file mode 100644
index 0000000..998e5ad
--- /dev/null
+++ b/examples/custom_error.rs
@@ -0,0 +1,40 @@
+use winnow::error::ErrMode;
+use winnow::error::ErrorKind;
+use winnow::error::ParserError;
+use winnow::prelude::*;
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum CustomError<I> {
+ MyError,
+ Nom(I, ErrorKind),
+}
+
+impl<I: Clone> ParserError<I> for CustomError<I> {
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
+ CustomError::Nom(input.clone(), kind)
+ }
+
+ fn append(self, _: &I, _: ErrorKind) -> Self {
+ self
+ }
+}
+
+pub fn parse<'s>(_input: &mut &'s str) -> PResult<&'s str, CustomError<&'s str>> {
+ Err(ErrMode::Backtrack(CustomError::MyError))
+}
+
+fn main() {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn it_works() {
+ let err = parse.parse_next(&mut "").unwrap_err();
+ match err {
+ ErrMode::Backtrack(e) => assert_eq!(e, CustomError::MyError),
+ _ => panic!("Unexpected error: {:?}", err),
+ }
+ }
+}
diff --git a/examples/http/bench.rs b/examples/http/bench.rs
new file mode 100644
index 0000000..a27a119
--- /dev/null
+++ b/examples/http/bench.rs
@@ -0,0 +1,36 @@
+mod parser;
+mod parser_streaming;
+
+fn one_test(c: &mut criterion::Criterion) {
+ let data = &b"GET / HTTP/1.1
+Host: www.reddit.com
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip, deflate
+Connection: keep-alive
+
+"[..];
+
+ let mut http_group = c.benchmark_group("http");
+ http_group.throughput(criterion::Throughput::Bytes(data.len() as u64));
+ http_group.bench_with_input(
+ criterion::BenchmarkId::new("complete", data.len()),
+ data,
+ |b, data| {
+ b.iter(|| parser::parse(data).unwrap());
+ },
+ );
+ http_group.bench_with_input(
+ criterion::BenchmarkId::new("streaming", data.len()),
+ data,
+ |b, data| {
+ b.iter(|| parser_streaming::parse(data).unwrap());
+ },
+ );
+
+ http_group.finish();
+}
+
+criterion::criterion_group!(http, one_test);
+criterion::criterion_main!(http);
diff --git a/examples/http/main.rs b/examples/http/main.rs
new file mode 100644
index 0000000..b0e480f
--- /dev/null
+++ b/examples/http/main.rs
@@ -0,0 +1,47 @@
+mod parser;
+
+fn main() -> Result<(), lexopt::Error> {
+ let args = Args::parse()?;
+
+ let input = args.input.as_deref().unwrap_or(
+ "GET / HTTP/1.1
+Host: www.reddit.com
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip, deflate
+Connection: keep-alive
+
+",
+ );
+
+ if let Some(result) = parser::parse(input.as_bytes()) {
+ println!(" {:#?}", result);
+ }
+
+ Ok(())
+}
+
+#[derive(Default)]
+struct Args {
+ input: Option<String>,
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Value(input) => {
+ res.input = Some(input.string()?);
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
diff --git a/examples/http/parser.rs b/examples/http/parser.rs
new file mode 100644
index 0000000..7f62c44
--- /dev/null
+++ b/examples/http/parser.rs
@@ -0,0 +1,138 @@
+use winnow::prelude::*;
+use winnow::{ascii::line_ending, combinator::repeat, token::take_while};
+
+pub type Stream<'i> = &'i [u8];
+
+#[rustfmt::skip]
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct Request<'a> {
+ method: &'a [u8],
+ uri: &'a [u8],
+ version: &'a [u8],
+}
+
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct Header<'a> {
+ name: &'a [u8],
+ value: Vec<&'a [u8]>,
+}
+
+pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
+ let mut buf = data;
+ let mut v = Vec::new();
+ loop {
+ match request(&mut buf) {
+ Ok(r) => {
+ v.push(r);
+
+ if buf.is_empty() {
+ //println!("{}", i);
+ break;
+ }
+ }
+ Err(e) => {
+ println!("error: {:?}", e);
+ return None;
+ }
+ }
+ }
+
+ Some(v)
+}
+
+fn request<'s>(input: &mut Stream<'s>) -> PResult<(Request<'s>, Vec<Header<'s>>)> {
+ let req = request_line(input)?;
+ let h = repeat(1.., message_header).parse_next(input)?;
+ let _ = line_ending.parse_next(input)?;
+
+ Ok((req, h))
+}
+
+fn request_line<'s>(input: &mut Stream<'s>) -> PResult<Request<'s>> {
+ let method = take_while(1.., is_token).parse_next(input)?;
+ let _ = take_while(1.., is_space).parse_next(input)?;
+ let uri = take_while(1.., is_not_space).parse_next(input)?;
+ let _ = take_while(1.., is_space).parse_next(input)?;
+ let version = http_version(input)?;
+ let _ = line_ending.parse_next(input)?;
+
+ Ok(Request {
+ method,
+ uri,
+ version,
+ })
+}
+
+fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> {
+ let _ = "HTTP/".parse_next(input)?;
+ let version = take_while(1.., is_version).parse_next(input)?;
+
+ Ok(version)
+}
+
+fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> {
+ let _ = take_while(1.., is_horizontal_space).parse_next(input)?;
+ let data = take_while(1.., not_line_ending).parse_next(input)?;
+ let _ = line_ending.parse_next(input)?;
+
+ Ok(data)
+}
+
+fn message_header<'s>(input: &mut Stream<'s>) -> PResult<Header<'s>> {
+ let name = take_while(1.., is_token).parse_next(input)?;
+ let _ = ':'.parse_next(input)?;
+ let value = repeat(1.., message_header_value).parse_next(input)?;
+
+ Ok(Header { name, value })
+}
+
+#[rustfmt::skip]
+#[allow(clippy::match_same_arms)]
+#[allow(clippy::match_like_matches_macro)]
+fn is_token(c: u8) -> bool {
+ match c {
+ 128..=255 => false,
+ 0..=31 => false,
+ b'(' => false,
+ b')' => false,
+ b'<' => false,
+ b'>' => false,
+ b'@' => false,
+ b',' => false,
+ b';' => false,
+ b':' => false,
+ b'\\' => false,
+ b'"' => false,
+ b'/' => false,
+ b'[' => false,
+ b']' => false,
+ b'?' => false,
+ b'=' => false,
+ b'{' => false,
+ b'}' => false,
+ b' ' => false,
+ _ => true,
+ }
+}
+
+fn is_version(c: u8) -> bool {
+ (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'
+}
diff --git a/examples/http/parser_streaming.rs b/examples/http/parser_streaming.rs
new file mode 100644
index 0000000..d59e6f8
--- /dev/null
+++ b/examples/http/parser_streaming.rs
@@ -0,0 +1,139 @@
+use winnow::{
+ ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while,
+};
+
+pub type Stream<'i> = Partial<&'i [u8]>;
+
+#[rustfmt::skip]
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct Request<'a> {
+ method: &'a [u8],
+ uri: &'a [u8],
+ version: &'a [u8],
+}
+
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct Header<'a> {
+ name: &'a [u8],
+ value: Vec<&'a [u8]>,
+}
+
+pub fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
+ let mut buf = Partial::new(data);
+ let mut v = Vec::new();
+ loop {
+ match request(&mut buf) {
+ Ok(r) => {
+ v.push(r);
+
+ if buf.is_empty() {
+ //println!("{}", i);
+ break;
+ }
+ }
+ Err(e) => {
+ println!("error: {:?}", e);
+ return None;
+ }
+ }
+ }
+
+ Some(v)
+}
+
+fn request<'s>(input: &mut Stream<'s>) -> PResult<(Request<'s>, Vec<Header<'s>>)> {
+ let req = request_line(input)?;
+ let h = repeat(1.., message_header).parse_next(input)?;
+ let _ = line_ending.parse_next(input)?;
+
+ Ok((req, h))
+}
+
+fn request_line<'s>(input: &mut Stream<'s>) -> PResult<Request<'s>> {
+ let method = take_while(1.., is_token).parse_next(input)?;
+ let _ = take_while(1.., is_space).parse_next(input)?;
+ let uri = take_while(1.., is_not_space).parse_next(input)?;
+ let _ = take_while(1.., is_space).parse_next(input)?;
+ let version = http_version(input)?;
+ let _ = line_ending.parse_next(input)?;
+
+ Ok(Request {
+ method,
+ uri,
+ version,
+ })
+}
+
+fn http_version<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> {
+ let _ = "HTTP/".parse_next(input)?;
+ let version = take_while(1.., is_version).parse_next(input)?;
+
+ Ok(version)
+}
+
+fn message_header_value<'s>(input: &mut Stream<'s>) -> PResult<&'s [u8]> {
+ let _ = take_while(1.., is_horizontal_space).parse_next(input)?;
+ let data = take_while(1.., not_line_ending).parse_next(input)?;
+ let _ = line_ending.parse_next(input)?;
+
+ Ok(data)
+}
+
+fn message_header<'s>(input: &mut Stream<'s>) -> PResult<Header<'s>> {
+ let name = take_while(1.., is_token).parse_next(input)?;
+ let _ = ':'.parse_next(input)?;
+ let value = repeat(1.., message_header_value).parse_next(input)?;
+
+ Ok(Header { name, value })
+}
+
+#[rustfmt::skip]
+#[allow(clippy::match_same_arms)]
+#[allow(clippy::match_like_matches_macro)]
+fn is_token(c: u8) -> bool {
+ match c {
+ 128..=255 => false,
+ 0..=31 => false,
+ b'(' => false,
+ b')' => false,
+ b'<' => false,
+ b'>' => false,
+ b'@' => false,
+ b',' => false,
+ b';' => false,
+ b':' => false,
+ b'\\' => false,
+ b'"' => false,
+ b'/' => false,
+ b'[' => false,
+ b']' => false,
+ b'?' => false,
+ b'=' => false,
+ b'{' => false,
+ b'}' => false,
+ b' ' => false,
+ _ => true,
+ }
+}
+
+fn is_version(c: u8) -> bool {
+ (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'
+}
diff --git a/examples/ini/bench.rs b/examples/ini/bench.rs
new file mode 100644
index 0000000..3c7eab8
--- /dev/null
+++ b/examples/ini/bench.rs
@@ -0,0 +1,61 @@
+use winnow::combinator::repeat;
+use winnow::prelude::*;
+
+mod parser;
+mod parser_str;
+
+fn bench_ini(c: &mut criterion::Criterion) {
+ let str = "[owner]
+name=John Doe
+organization=Acme Widgets Inc.
+
+[database]
+server=192.0.2.62
+port=143
+file=payroll.dat
+\0";
+
+ let mut group = c.benchmark_group("ini");
+ group.throughput(criterion::Throughput::Bytes(str.len() as u64));
+ group.bench_function(criterion::BenchmarkId::new("bytes", str.len()), |b| {
+ b.iter(|| parser::categories.parse_peek(str.as_bytes()).unwrap());
+ });
+ group.bench_function(criterion::BenchmarkId::new("str", str.len()), |b| {
+ b.iter(|| parser_str::categories.parse_peek(str).unwrap())
+ });
+}
+
+fn bench_ini_keys_and_values(c: &mut criterion::Criterion) {
+ let str = "server=192.0.2.62
+port=143
+file=payroll.dat
+\0";
+
+ fn acc<'s>(i: &mut parser::Stream<'s>) -> PResult<Vec<(&'s str, &'s str)>> {
+ repeat(0.., parser::key_value).parse_next(i)
+ }
+
+ let mut group = c.benchmark_group("ini keys and values");
+ group.throughput(criterion::Throughput::Bytes(str.len() as u64));
+ group.bench_function(criterion::BenchmarkId::new("bytes", str.len()), |b| {
+ b.iter(|| acc.parse_peek(str.as_bytes()).unwrap());
+ });
+}
+
+fn bench_ini_key_value(c: &mut criterion::Criterion) {
+ let str = "server=192.0.2.62\n";
+
+ let mut group = c.benchmark_group("ini key value");
+ group.throughput(criterion::Throughput::Bytes(str.len() as u64));
+ group.bench_function(criterion::BenchmarkId::new("bytes", str.len()), |b| {
+ b.iter(|| parser::key_value.parse_peek(str.as_bytes()).unwrap());
+ });
+}
+
+criterion::criterion_group!(
+ benches,
+ bench_ini,
+ bench_ini_keys_and_values,
+ bench_ini_key_value
+);
+criterion::criterion_main!(benches);
diff --git a/examples/ini/main.rs b/examples/ini/main.rs
new file mode 100644
index 0000000..8f61d18
--- /dev/null
+++ b/examples/ini/main.rs
@@ -0,0 +1,60 @@
+use winnow::prelude::*;
+
+mod parser;
+mod parser_str;
+
+fn main() -> Result<(), lexopt::Error> {
+ let args = Args::parse()?;
+
+ let input = args.input.as_deref().unwrap_or("1 + 1");
+
+ if args.binary {
+ match parser::categories.parse(input.as_bytes()) {
+ Ok(result) => {
+ println!(" {:?}", result);
+ }
+ Err(err) => {
+ println!(" {:?}", err);
+ }
+ }
+ } else {
+ match parser_str::categories.parse(input) {
+ Ok(result) => {
+ println!(" {:?}", result);
+ }
+ Err(err) => {
+ println!(" {}", err);
+ }
+ }
+ }
+
+ Ok(())
+}
+
+#[derive(Default)]
+struct Args {
+ input: Option<String>,
+ binary: bool,
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Long("binary") => {
+ res.binary = true;
+ }
+ Value(input) => {
+ res.input = Some(input.string()?);
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
diff --git a/examples/ini/parser.rs b/examples/ini/parser.rs
new file mode 100644
index 0000000..a782d6e
--- /dev/null
+++ b/examples/ini/parser.rs
@@ -0,0 +1,146 @@
+use std::collections::HashMap;
+use std::str;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::{alphanumeric1 as alphanumeric, multispace0 as multispace, space0 as space},
+ combinator::opt,
+ combinator::repeat,
+ combinator::{delimited, separated_pair, terminated},
+ token::take_while,
+};
+
+pub type Stream<'i> = &'i [u8];
+
+pub fn categories<'s>(i: &mut Stream<'s>) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> {
+ repeat(
+ 0..,
+ separated_pair(
+ category,
+ opt(multispace),
+ repeat(0.., terminated(key_value, opt(multispace))),
+ ),
+ )
+ .parse_next(i)
+}
+
+fn category<'s>(i: &mut Stream<'s>) -> PResult<&'s str> {
+ delimited('[', take_while(0.., |c| c != b']'), ']')
+ .try_map(str::from_utf8)
+ .parse_next(i)
+}
+
+pub fn key_value<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, &'s str)> {
+ let key = alphanumeric.try_map(str::from_utf8).parse_next(i)?;
+ let _ = (opt(space), '=', opt(space)).parse_next(i)?;
+ let val = take_while(0.., |c| c != b'\n' && c != b';')
+ .try_map(str::from_utf8)
+ .parse_next(i)?;
+ let _ = opt((';', take_while(0.., |c| c != b'\n'))).parse_next(i)?;
+ Ok((key, val))
+}
+
+#[test]
+fn parse_category_test() {
+ let ini_file = &b"[category]
+
+parameter=value
+key = value2"[..];
+
+ let ini_without_category = &b"\n\nparameter=value
+key = value2"[..];
+
+ let res = category.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_category, "category")));
+}
+
+#[test]
+fn parse_key_value_test() {
+ let ini_file = &b"parameter=value
+key = value2"[..];
+
+ let ini_without_key_value = &b"\nkey = value2"[..];
+
+ let res = key_value.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
+}
+
+#[test]
+fn parse_key_value_with_space_test() {
+ let ini_file = &b"parameter = value
+key = value2"[..];
+
+ let ini_without_key_value = &b"\nkey = value2"[..];
+
+ let res = key_value.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
+}
+
+#[test]
+fn parse_key_value_with_comment_test() {
+ let ini_file = &b"parameter=value;abc
+key = value2"[..];
+
+ let ini_without_key_value = &b"\nkey = value2"[..];
+
+ let res = key_value.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, (o1, o2))) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
+}
+
+#[test]
+fn parse_multiple_categories_test() {
+ let ini_file = &b"[abcd]
+
+parameter=value;abc
+
+key = value2
+
+[category]
+parameter3=value3
+key4 = value4
+"[..];
+
+ let ini_after_parser = &b""[..];
+
+ let res = categories.parse_peek(ini_file);
+ //println!("{:?}", res);
+ match res {
+ Ok((i, ref o)) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
+ _ => println!("error"),
+ }
+
+ let mut expected_1: HashMap<&str, &str> = HashMap::new();
+ expected_1.insert("parameter", "value");
+ expected_1.insert("key", "value2");
+ let mut expected_2: HashMap<&str, &str> = HashMap::new();
+ expected_2.insert("parameter3", "value3");
+ expected_2.insert("key4", "value4");
+ let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
+ expected_h.insert("abcd", expected_1);
+ expected_h.insert("category", expected_2);
+ assert_eq!(res, Ok((ini_after_parser, expected_h)));
+}
diff --git a/examples/ini/parser_str.rs b/examples/ini/parser_str.rs
new file mode 100644
index 0000000..8f7b9ce
--- /dev/null
+++ b/examples/ini/parser_str.rs
@@ -0,0 +1,208 @@
+use std::collections::HashMap;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::{alphanumeric1 as alphanumeric, space0 as space},
+ combinator::opt,
+ combinator::repeat,
+ combinator::{delimited, terminated},
+ token::{take_till0, take_while},
+};
+
+pub type Stream<'i> = &'i str;
+
+pub fn categories<'s>(
+ input: &mut Stream<'s>,
+) -> PResult<HashMap<&'s str, HashMap<&'s str, &'s str>>> {
+ repeat(0.., category_and_keys).parse_next(input)
+}
+
+fn category_and_keys<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, HashMap<&'s str, &'s str>)> {
+ (category, keys_and_values).parse_next(i)
+}
+
+fn category<'s>(i: &mut Stream<'s>) -> PResult<&'s str> {
+ terminated(
+ delimited('[', take_while(0.., |c| c != ']'), ']'),
+ opt(take_while(1.., [' ', '\r', '\n'])),
+ )
+ .parse_next(i)
+}
+
+fn keys_and_values<'s>(input: &mut Stream<'s>) -> PResult<HashMap<&'s str, &'s str>> {
+ repeat(0.., key_value).parse_next(input)
+}
+
+fn key_value<'s>(i: &mut Stream<'s>) -> PResult<(&'s str, &'s str)> {
+ let key = alphanumeric.parse_next(i)?;
+ let _ = (opt(space), "=", opt(space)).parse_next(i)?;
+ let val = take_till0(is_line_ending_or_comment).parse_next(i)?;
+ let _ = opt(space).parse_next(i)?;
+ let _ = opt((";", not_line_ending)).parse_next(i)?;
+ let _ = opt(space_or_line_ending).parse_next(i)?;
+
+ Ok((key, val))
+}
+
+fn is_line_ending_or_comment(chr: char) -> bool {
+ chr == ';' || chr == '\n'
+}
+
+fn not_line_ending<'s>(i: &mut Stream<'s>) -> PResult<&'s str> {
+ take_while(0.., |c| c != '\r' && c != '\n').parse_next(i)
+}
+
+fn space_or_line_ending<'s>(i: &mut Stream<'s>) -> PResult<&'s str> {
+ take_while(1.., [' ', '\r', '\n']).parse_next(i)
+}
+
+#[test]
+fn parse_category_test() {
+ let ini_file = "[category]
+
+parameter=value
+key = value2";
+
+ let ini_without_category = "parameter=value
+key = value2";
+
+ let res = category.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, o)) => println!("i: {} | o: {:?}", i, o),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_category, "category")));
+}
+
+#[test]
+fn parse_key_value_test() {
+ let ini_file = "parameter=value
+key = value2";
+
+ let ini_without_key_value = "key = value2";
+
+ let res = key_value.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
+}
+
+#[test]
+fn parse_key_value_with_space_test() {
+ let ini_file = "parameter = value
+key = value2";
+
+ let ini_without_key_value = "key = value2";
+
+ let res = key_value.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
+}
+
+#[test]
+fn parse_key_value_with_comment_test() {
+ let ini_file = "parameter=value;abc
+key = value2";
+
+ let ini_without_key_value = "key = value2";
+
+ let res = key_value.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, (o1, o2))) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
+ _ => println!("error"),
+ }
+
+ assert_eq!(res, Ok((ini_without_key_value, ("parameter", "value"))));
+}
+
+#[test]
+fn parse_multiple_keys_and_values_test() {
+ let ini_file = "parameter=value;abc
+
+key = value2
+
+[category]";
+
+ let ini_without_key_value = "[category]";
+
+ let res = keys_and_values.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
+ _ => println!("error"),
+ }
+
+ let mut expected: HashMap<&str, &str> = HashMap::new();
+ expected.insert("parameter", "value");
+ expected.insert("key", "value2");
+ assert_eq!(res, Ok((ini_without_key_value, expected)));
+}
+
+#[test]
+fn parse_category_then_multiple_keys_and_values_test() {
+ //FIXME: there can be an empty line or a comment line after a category
+ let ini_file = "[abcd]
+parameter=value;abc
+
+key = value2
+
+[category]";
+
+ let ini_after_parser = "[category]";
+
+ let res = category_and_keys.parse_peek(ini_file);
+ println!("{:?}", res);
+ match res {
+ Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
+ _ => println!("error"),
+ }
+
+ let mut expected_h: HashMap<&str, &str> = HashMap::new();
+ expected_h.insert("parameter", "value");
+ expected_h.insert("key", "value2");
+ assert_eq!(res, Ok((ini_after_parser, ("abcd", expected_h))));
+}
+
+#[test]
+fn parse_multiple_categories_test() {
+ let ini_file = "[abcd]
+
+parameter=value;abc
+
+key = value2
+
+[category]
+parameter3=value3
+key4 = value4
+";
+
+ let res = categories.parse_peek(ini_file);
+ //println!("{:?}", res);
+ match res {
+ Ok((i, ref o)) => println!("i: {} | o: {:?}", i, o),
+ _ => println!("error"),
+ }
+
+ let mut expected_1: HashMap<&str, &str> = HashMap::new();
+ expected_1.insert("parameter", "value");
+ expected_1.insert("key", "value2");
+ let mut expected_2: HashMap<&str, &str> = HashMap::new();
+ expected_2.insert("parameter3", "value3");
+ expected_2.insert("key4", "value4");
+ let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
+ expected_h.insert("abcd", expected_1);
+ expected_h.insert("category", expected_2);
+ assert_eq!(res, Ok(("", expected_h)));
+}
diff --git a/examples/iterator.rs b/examples/iterator.rs
new file mode 100644
index 0000000..5c8c731
--- /dev/null
+++ b/examples/iterator.rs
@@ -0,0 +1,77 @@
+use std::collections::HashMap;
+use std::iter::Iterator;
+
+use winnow::ascii::alphanumeric1;
+use winnow::combinator::iterator;
+use winnow::combinator::{separated_pair, terminated};
+use winnow::prelude::*;
+
+fn main() {
+ let mut data = "abcabcabcabc";
+
+ fn parser<'s>(i: &mut &'s str) -> PResult<&'s str> {
+ "abc".parse_next(i)
+ }
+
+ // `from_fn` (available from Rust 1.34) can create an iterator
+ // from a closure
+ let it = std::iter::from_fn(move || {
+ match parser.parse_next(&mut data) {
+ // when successful, a parser returns a tuple of
+ // the remaining input and the output value.
+ // So we replace the captured input data with the
+ // remaining input, to be parsed on the next call
+ Ok(o) => Some(o),
+ _ => None,
+ }
+ });
+
+ for value in it {
+ println!("parser returned: {}", value);
+ }
+
+ println!("\n********************\n");
+
+ let mut data = "abcabcabcabc";
+
+ // if `from_fn` is not available, it is possible to fold
+ // over an iterator of functions
+ let res = std::iter::repeat(parser)
+ .take(3)
+ .try_fold(Vec::new(), |mut acc, mut parser| {
+ parser.parse_next(&mut data).map(|o| {
+ acc.push(o);
+ acc
+ })
+ });
+
+ // will print "parser iterator returned: Ok(("abc", ["abc", "abc", "abc"]))"
+ println!("\nparser iterator returned: {:?}", res);
+
+ println!("\n********************\n");
+
+ let data = "key1:value1,key2:value2,key3:value3,;";
+
+ // `winnow::combinator::iterator` will return an iterator
+ // producing the parsed values. Compared to the previous
+ // solutions:
+ // - we can work with a normal iterator like `from_fn`
+ // - we can get the remaining input afterwards, like with the `try_fold` trick
+ let mut winnow_it = iterator(
+ data,
+ terminated(separated_pair(alphanumeric1, ":", alphanumeric1), ","),
+ );
+
+ let res = winnow_it
+ .map(|(k, v)| (k.to_uppercase(), v))
+ .collect::<HashMap<_, _>>();
+
+ let parser_result: PResult<(_, _), ()> = winnow_it.finish();
+ let (remaining_input, ()) = parser_result.unwrap();
+
+ // will print "iterator returned {"key1": "value1", "key3": "value3", "key2": "value2"}, remaining input is ';'"
+ println!(
+ "iterator returned {:?}, remaining input is '{}'",
+ res, remaining_input
+ );
+}
diff --git a/examples/json/bench.rs b/examples/json/bench.rs
new file mode 100644
index 0000000..d074ba5
--- /dev/null
+++ b/examples/json/bench.rs
@@ -0,0 +1,70 @@
+use winnow::prelude::*;
+use winnow::Partial;
+
+mod json;
+mod parser;
+mod parser_dispatch;
+mod parser_partial;
+
+fn json_bench(c: &mut criterion::Criterion) {
+ let data = [("small", SMALL), ("canada", CANADA)];
+ let mut group = c.benchmark_group("json");
+ for (name, sample) in data {
+ let len = sample.len();
+ group.throughput(criterion::Throughput::Bytes(len as u64));
+
+ group.bench_with_input(criterion::BenchmarkId::new("basic", name), &len, |b, _| {
+ type Error<'i> = winnow::error::InputError<parser::Stream<'i>>;
+
+ b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
+ });
+ group.bench_with_input(criterion::BenchmarkId::new("unit", name), &len, |b, _| {
+ type Error<'i> = ();
+
+ b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
+ });
+ group.bench_with_input(
+ criterion::BenchmarkId::new("context", name),
+ &len,
+ |b, _| {
+ type Error<'i> = winnow::error::ContextError<parser::Stream<'i>>;
+
+ b.iter(|| parser::json::<Error>.parse_peek(sample).unwrap());
+ },
+ );
+ group.bench_with_input(
+ criterion::BenchmarkId::new("dispatch", name),
+ &len,
+ |b, _| {
+ type Error<'i> = winnow::error::InputError<parser_dispatch::Stream<'i>>;
+
+ b.iter(|| parser_dispatch::json::<Error>.parse_peek(sample).unwrap());
+ },
+ );
+ group.bench_with_input(
+ criterion::BenchmarkId::new("streaming", name),
+ &len,
+ |b, _| {
+ type Error<'i> = winnow::error::InputError<parser_partial::Stream<'i>>;
+
+ b.iter(|| {
+ parser_partial::json::<Error>
+ .parse_peek(Partial::new(sample))
+ .unwrap()
+ });
+ },
+ );
+ }
+ group.finish();
+}
+
+const SMALL: &str = " { \"a\"\t: 42,
+ \"b\": [ \"x\", \"y\", 12 ,\"\\u2014\", \"\\uD83D\\uDE10\"] ,
+ \"c\": { \"hello\" : \"world\"
+ }
+ } ";
+
+const CANADA: &str = include_str!("../../third_party/nativejson-benchmark/data/canada.json");
+
+criterion::criterion_group!(benches, json_bench,);
+criterion::criterion_main!(benches);
diff --git a/examples/json/json.rs b/examples/json/json.rs
new file mode 100644
index 0000000..6912d60
--- /dev/null
+++ b/examples/json/json.rs
@@ -0,0 +1,11 @@
+use std::collections::HashMap;
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum JsonValue {
+ Null,
+ Boolean(bool),
+ Str(String),
+ Num(f64),
+ Array(Vec<JsonValue>),
+ Object(HashMap<String, JsonValue>),
+}
diff --git a/examples/json/main.rs b/examples/json/main.rs
new file mode 100644
index 0000000..be431e8
--- /dev/null
+++ b/examples/json/main.rs
@@ -0,0 +1,98 @@
+mod json;
+mod parser;
+mod parser_dispatch;
+#[allow(dead_code)]
+mod parser_partial;
+
+use winnow::error::ErrorKind;
+use winnow::prelude::*;
+
+fn main() -> Result<(), lexopt::Error> {
+ let args = Args::parse()?;
+
+ let data = args.input.as_deref().unwrap_or(if args.invalid {
+ " { \"a\"\t: 42,
+ \"b\": [ \"x\", \"y\", 12 ] ,
+ \"c\": { 1\"hello\" : \"world\"
+ }
+ } "
+ } else {
+ " { \"a\"\t: 42,
+ \"b\": [ \"x\", \"y\", 12 ] ,
+ \"c\": { \"hello\" : \"world\"
+ }
+ } "
+ });
+
+ let result = match args.implementation {
+ Impl::Naive => parser::json::<ErrorKind>.parse(data),
+ Impl::Dispatch => parser_dispatch::json::<ErrorKind>.parse(data),
+ };
+ match result {
+ Ok(json) => {
+ println!("{:#?}", json);
+ }
+ Err(err) => {
+ if args.pretty {
+ println!("{}", err);
+ } else {
+ println!("{:#?}", err);
+ }
+ }
+ }
+
+ Ok(())
+}
+
+#[derive(Default)]
+struct Args {
+ input: Option<String>,
+ invalid: bool,
+ pretty: bool,
+ implementation: Impl,
+}
+
+enum Impl {
+ Naive,
+ Dispatch,
+}
+
+impl Default for Impl {
+ fn default() -> Self {
+ Self::Naive
+ }
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Long("invalid") => {
+ res.invalid = true;
+ }
+ Long("pretty") => {
+ // Only case where pretty matters
+ res.pretty = true;
+ res.invalid = true;
+ }
+ Long("impl") => {
+ res.implementation = args.value()?.parse_with(|s| match s {
+ "naive" => Ok(Impl::Naive),
+ "dispatch" => Ok(Impl::Dispatch),
+ _ => Err("expected `naive`, `dispatch`"),
+ })?;
+ }
+ Value(input) => {
+ res.input = Some(input.string()?);
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
diff --git a/examples/json/parser.rs b/examples/json/parser.rs
new file mode 100644
index 0000000..8aa3bd3
--- /dev/null
+++ b/examples/json/parser.rs
@@ -0,0 +1,322 @@
+use std::collections::HashMap;
+use std::str;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::float,
+ combinator::alt,
+ combinator::cut_err,
+ combinator::{delimited, preceded, separated_pair, terminated},
+ combinator::{fold_repeat, separated0},
+ error::{AddContext, ParserError},
+ token::{any, none_of, take, take_while},
+};
+
+use crate::json::JsonValue;
+
+pub type Stream<'i> = &'i str;
+
+/// The root element of a JSON parser is any value
+///
+/// A parser has the following signature:
+/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
+/// `type PResult<O, E = (I, ErrorKind)> = Result<O, Err<E>>;`
+///
+/// most of the times you can ignore the error type and use the default (but this
+/// examples shows custom error types later on!)
+///
+/// Here we use `&str` as input type, but parsers can be generic over
+/// the input type, work directly with `&[u8]`, or any other type that
+/// implements the required traits.
+pub fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ delimited(ws, json_value, ws).parse_next(input)
+}
+
+/// `alt` is a combinator that tries multiple parsers one by one, until
+/// one of them succeeds
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ // `alt` combines the each value parser. It returns the result of the first
+ // successful parser, or an error
+ alt((
+ null.value(JsonValue::Null),
+ boolean.map(JsonValue::Boolean),
+ string.map(JsonValue::Str),
+ float.map(JsonValue::Num),
+ array.map(JsonValue::Array),
+ object.map(JsonValue::Object),
+ ))
+ .parse_next(input)
+}
+
+/// `tag(string)` generates a parser that recognizes the argument string.
+///
+/// This also shows returning a sub-slice of the original input
+fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // This is a parser that returns `"null"` if it sees the string "null", and
+ // an error otherwise
+ "null".parse_next(input)
+}
+
+/// We can combine `tag` with other functions, like `value` which returns a given constant value on
+/// success.
+fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> {
+ // This is a parser that returns `true` if it sees the string "true", and
+ // an error otherwise
+ let parse_true = "true".value(true);
+
+ // This is a parser that returns `false` if it sees the string "false", and
+ // an error otherwise
+ let parse_false = "false".value(false);
+
+ alt((parse_true, parse_false)).parse_next(input)
+}
+
+/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// character, before the string (using `preceded`) and after the string (using `terminated`).
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<String, E> {
+ preceded(
+ '\"',
+ // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to
+ // combinators like `alt` that they should not try other parsers. We were in the
+ // right branch (since we found the `"` character) but encountered an error when
+ // parsing the string
+ cut_err(terminated(
+ fold_repeat(0.., character, String::new, |mut string, c| {
+ string.push(c);
+ string
+ }),
+ '\"',
+ )),
+ )
+ // `context` lets you add a static string to errors to provide more information in the
+ // error chain (to indicate which parser had an error)
+ .context("string")
+ .parse_next(input)
+}
+
+/// You can mix the above declarative parsing with an imperative style to handle more unique cases,
+/// like escaping
+fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ let c = none_of('\"').parse_next(input)?;
+ if c == '\\' {
+ alt((
+ any.verify_map(|c| {
+ Some(match c {
+ '"' | '\\' | '/' => c,
+ 'b' => '\x08',
+ 'f' => '\x0C',
+ 'n' => '\n',
+ 'r' => '\r',
+ 't' => '\t',
+ _ => return None,
+ })
+ }),
+ preceded('u', unicode_escape),
+ ))
+ .parse_next(input)
+ } else {
+ Ok(c)
+ }
+}
+
+fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ alt((
+ // Not a surrogate
+ u16_hex
+ .verify(|cp| !(0xD800..0xE000).contains(cp))
+ .map(|cp| cp as u32),
+ // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details
+ separated_pair(u16_hex, "\\u", u16_hex)
+ .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low))
+ .map(|(high, low)| {
+ let high_ten = (high as u32) - 0xD800;
+ let low_ten = (low as u32) - 0xDC00;
+ (high_ten << 10) + low_ten + 0x10000
+ }),
+ ))
+ .verify_map(
+ // Could be probably replaced with .unwrap() or _unchecked due to the verify checks
+ std::char::from_u32,
+ )
+ .parse_next(input)
+}
+
+fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> {
+ take(4usize)
+ .verify_map(|s| u16::from_str_radix(s, 16).ok())
+ .parse_next(input)
+}
+
+/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly,
+/// accumulating results in a `Vec`, until it encounters an error.
+/// If you want more control on the parser application, check out the `iterator`
+/// combinator (cf `examples/iterator.rs`)
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<Vec<JsonValue>, E> {
+ preceded(
+ ('[', ws),
+ cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))),
+ )
+ .context("array")
+ .parse_next(input)
+}
+
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<HashMap<String, JsonValue>, E> {
+ preceded(
+ ('{', ws),
+ cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))),
+ )
+ .context("object")
+ .parse_next(input)
+}
+
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<(String, JsonValue), E> {
+ separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
+}
+
+/// Parser combinators are constructed from the bottom up:
+/// first we write parsers for the smallest elements (here a space character),
+/// then we'll combine them in larger parsers
+fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // Combinators like `take_while` return a function. That function is the
+ // parser,to which we can pass the input
+ take_while(0.., WS).parse_next(input)
+}
+
+const WS: &[char] = &[' ', '\t', '\r', '\n'];
+
+#[cfg(test)]
+mod test {
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ use super::*;
+
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ type Error<'i> = winnow::error::InputError<&'i str>;
+
+ #[test]
+ fn json_string() {
+ assert_eq!(
+ string::<Error<'_>>.parse_peek("\"\""),
+ Ok(("", "".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek("\"abc\""),
+ Ok(("", "abc".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>
+ .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""),
+ Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string())),
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""),
+ Ok(("", "😐".to_string()))
+ );
+
+ assert!(string::<Error<'_>>.parse_peek("\"").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"abc").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\\"").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\u123\"").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\uD800\"").is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek("\"\\uD800\\uD800\"")
+ .is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\uDC00\"").is_err());
+ }
+
+ #[test]
+ fn json_object() {
+ use JsonValue::{Num, Object, Str};
+
+ let input = r#"{"a":42,"b":"x"}"#;
+
+ let expected = Object(
+ vec![
+ ("a".to_string(), Num(42.0)),
+ ("b".to_string(), Str("x".to_string())),
+ ]
+ .into_iter()
+ .collect(),
+ );
+
+ assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+ }
+
+ #[test]
+ fn json_array() {
+ use JsonValue::{Array, Num, Str};
+
+ let input = r#"[42,"x"]"#;
+
+ let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+
+ assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+ }
+
+ #[test]
+ fn json_whitespace() {
+ use JsonValue::{Array, Boolean, Null, Num, Object, Str};
+
+ let input = r#"
+ {
+ "null" : null,
+ "true" :true ,
+ "false": false ,
+ "number" : 123e4 ,
+ "string" : " abc 123 " ,
+ "array" : [ false , 1 , "two" ] ,
+ "object" : { "a" : 1.0 , "b" : "c" } ,
+ "empty_array" : [ ] ,
+ "empty_object" : { }
+ }
+ "#;
+
+ assert_eq!(
+ json::<Error<'_>>.parse_peek(input),
+ Ok((
+ "",
+ Object(
+ vec![
+ ("null".to_string(), Null),
+ ("true".to_string(), Boolean(true)),
+ ("false".to_string(), Boolean(false)),
+ ("number".to_string(), Num(123e4)),
+ ("string".to_string(), Str(" abc 123 ".to_string())),
+ (
+ "array".to_string(),
+ Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+ ),
+ (
+ "object".to_string(),
+ Object(
+ vec![
+ ("a".to_string(), Num(1.0)),
+ ("b".to_string(), Str("c".to_string())),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ),
+ ("empty_array".to_string(), Array(vec![]),),
+ ("empty_object".to_string(), Object(HashMap::new()),),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ))
+ );
+ }
+}
diff --git a/examples/json/parser_dispatch.rs b/examples/json/parser_dispatch.rs
new file mode 100644
index 0000000..6fa722b
--- /dev/null
+++ b/examples/json/parser_dispatch.rs
@@ -0,0 +1,329 @@
+use std::collections::HashMap;
+use std::str;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::float,
+ combinator::cut_err,
+ combinator::fail,
+ combinator::peek,
+ combinator::success,
+ combinator::{alt, dispatch},
+ combinator::{delimited, preceded, separated_pair, terminated},
+ combinator::{fold_repeat, separated0},
+ error::{AddContext, ParserError},
+ token::{any, none_of, take, take_while},
+};
+
+use crate::json::JsonValue;
+
+pub type Stream<'i> = &'i str;
+
+/// The root element of a JSON parser is any value
+///
+/// A parser has the following signature:
+/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
+/// `type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;`
+///
+/// most of the times you can ignore the error type and use the default (but this
+/// examples shows custom error types later on!)
+///
+/// Here we use `&str` as input type, but parsers can be generic over
+/// the input type, work directly with `&[u8]`, or any other type that
+/// implements the required traits.
+pub fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ delimited(ws, json_value, ws).parse_next(input)
+}
+
+/// `alt` is a combinator that tries multiple parsers one by one, until
+/// one of them succeeds
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ // `dispatch` gives you `match`-like behavior compared to `alt` successively trying different
+ // implementations.
+ dispatch!(peek(any);
+ 'n' => null.value(JsonValue::Null),
+ 't' => true_.map(JsonValue::Boolean),
+ 'f' => false_.map(JsonValue::Boolean),
+ '"' => string.map(JsonValue::Str),
+ '+' => float.map(JsonValue::Num),
+ '-' => float.map(JsonValue::Num),
+ '0'..='9' => float.map(JsonValue::Num),
+ '[' => array.map(JsonValue::Array),
+ '{' => object.map(JsonValue::Object),
+ _ => fail,
+ )
+ .parse_next(input)
+}
+
+/// `tag(string)` generates a parser that recognizes the argument string.
+///
+/// This also shows returning a sub-slice of the original input
+fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // This is a parser that returns `"null"` if it sees the string "null", and
+ // an error otherwise
+ "null".parse_next(input)
+}
+
+/// We can combine `tag` with other functions, like `value` which returns a given constant value on
+/// success.
+fn true_<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> {
+ // This is a parser that returns `true` if it sees the string "true", and
+ // an error otherwise
+ "true".value(true).parse_next(input)
+}
+
+/// We can combine `tag` with other functions, like `value` which returns a given constant value on
+/// success.
+fn false_<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> {
+ // This is a parser that returns `false` if it sees the string "false", and
+ // an error otherwise
+ "false".value(false).parse_next(input)
+}
+
+/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// character, before the string (using `preceded`) and after the string (using `terminated`).
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<String, E> {
+ preceded(
+ '\"',
+ // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to
+ // combinators like `alt` that they should not try other parsers. We were in the
+ // right branch (since we found the `"` character) but encountered an error when
+ // parsing the string
+ cut_err(terminated(
+ fold_repeat(0.., character, String::new, |mut string, c| {
+ string.push(c);
+ string
+ }),
+ '\"',
+ )),
+ )
+ // `context` lets you add a static string to errors to provide more information in the
+ // error chain (to indicate which parser had an error)
+ .context("string")
+ .parse_next(input)
+}
+
+/// You can mix the above declarative parsing with an imperative style to handle more unique cases,
+/// like escaping
+fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ let c = none_of('\"').parse_next(input)?;
+ if c == '\\' {
+ dispatch!(any;
+ '"' => success('"'),
+ '\\' => success('\\'),
+ '/' => success('/'),
+ 'b' => success('\x08'),
+ 'f' => success('\x0C'),
+ 'n' => success('\n'),
+ 'r' => success('\r'),
+ 't' => success('\t'),
+ 'u' => unicode_escape,
+ _ => fail,
+ )
+ .parse_next(input)
+ } else {
+ Ok(c)
+ }
+}
+
+fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ alt((
+ // Not a surrogate
+ u16_hex
+ .verify(|cp| !(0xD800..0xE000).contains(cp))
+ .map(|cp| cp as u32),
+ // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details
+ separated_pair(u16_hex, "\\u", u16_hex)
+ .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low))
+ .map(|(high, low)| {
+ let high_ten = (high as u32) - 0xD800;
+ let low_ten = (low as u32) - 0xDC00;
+ (high_ten << 10) + low_ten + 0x10000
+ }),
+ ))
+ .verify_map(
+ // Could be probably replaced with .unwrap() or _unchecked due to the verify checks
+ std::char::from_u32,
+ )
+ .parse_next(input)
+}
+
+fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> {
+ take(4usize)
+ .verify_map(|s| u16::from_str_radix(s, 16).ok())
+ .parse_next(input)
+}
+
+/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly,
+/// accumulating results in a `Vec`, until it encounters an error.
+/// If you want more control on the parser application, check out the `iterator`
+/// combinator (cf `examples/iterator.rs`)
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<Vec<JsonValue>, E> {
+ preceded(
+ ('[', ws),
+ cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))),
+ )
+ .context("array")
+ .parse_next(input)
+}
+
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<HashMap<String, JsonValue>, E> {
+ preceded(
+ ('{', ws),
+ cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))),
+ )
+ .context("object")
+ .parse_next(input)
+}
+
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<(String, JsonValue), E> {
+ separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
+}
+
+/// Parser combinators are constructed from the bottom up:
+/// first we write parsers for the smallest elements (here a space character),
+/// then we'll combine them in larger parsers
+fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // Combinators like `take_while` return a function. That function is the
+ // parser,to which we can pass the input
+ take_while(0.., WS).parse_next(input)
+}
+
+const WS: &[char] = &[' ', '\t', '\r', '\n'];
+
+#[cfg(test)]
+mod test {
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ use super::*;
+
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ type Error<'i> = winnow::error::InputError<&'i str>;
+
+ #[test]
+ fn json_string() {
+ assert_eq!(
+ string::<Error<'_>>.parse_peek("\"\""),
+ Ok(("", "".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek("\"abc\""),
+ Ok(("", "abc".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>
+ .parse_peek("\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""),
+ Ok(("", "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string())),
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek("\"\\uD83D\\uDE10\""),
+ Ok(("", "😐".to_string()))
+ );
+
+ assert!(string::<Error<'_>>.parse_peek("\"").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"abc").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\\"").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\u123\"").is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\uD800\"").is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek("\"\\uD800\\uD800\"")
+ .is_err());
+ assert!(string::<Error<'_>>.parse_peek("\"\\uDC00\"").is_err());
+ }
+
+ #[test]
+ fn json_object() {
+ use JsonValue::{Num, Object, Str};
+
+ let input = r#"{"a":42,"b":"x"}"#;
+
+ let expected = Object(
+ vec![
+ ("a".to_string(), Num(42.0)),
+ ("b".to_string(), Str("x".to_string())),
+ ]
+ .into_iter()
+ .collect(),
+ );
+
+ assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+ }
+
+ #[test]
+ fn json_array() {
+ use JsonValue::{Array, Num, Str};
+
+ let input = r#"[42,"x"]"#;
+
+ let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+
+ assert_eq!(json::<Error<'_>>.parse_peek(input), Ok(("", expected)));
+ }
+
+ #[test]
+ fn json_whitespace() {
+ use JsonValue::{Array, Boolean, Null, Num, Object, Str};
+
+ let input = r#"
+ {
+ "null" : null,
+ "true" :true ,
+ "false": false ,
+ "number" : 123e4 ,
+ "string" : " abc 123 " ,
+ "array" : [ false , 1 , "two" ] ,
+ "object" : { "a" : 1.0 , "b" : "c" } ,
+ "empty_array" : [ ] ,
+ "empty_object" : { }
+ }
+ "#;
+
+ assert_eq!(
+ json::<Error<'_>>.parse_peek(input),
+ Ok((
+ "",
+ Object(
+ vec![
+ ("null".to_string(), Null),
+ ("true".to_string(), Boolean(true)),
+ ("false".to_string(), Boolean(false)),
+ ("number".to_string(), Num(123e4)),
+ ("string".to_string(), Str(" abc 123 ".to_string())),
+ (
+ "array".to_string(),
+ Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+ ),
+ (
+ "object".to_string(),
+ Object(
+ vec![
+ ("a".to_string(), Num(1.0)),
+ ("b".to_string(), Str("c".to_string())),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ),
+ ("empty_array".to_string(), Array(vec![]),),
+ ("empty_object".to_string(), Object(HashMap::new()),),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ))
+ );
+ }
+}
diff --git a/examples/json/parser_partial.rs b/examples/json/parser_partial.rs
new file mode 100644
index 0000000..3538d8e
--- /dev/null
+++ b/examples/json/parser_partial.rs
@@ -0,0 +1,348 @@
+use std::collections::HashMap;
+use std::str;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::float,
+ combinator::alt,
+ combinator::{cut_err, rest},
+ combinator::{delimited, preceded, separated_pair, terminated},
+ combinator::{fold_repeat, separated0},
+ error::{AddContext, ParserError},
+ stream::Partial,
+ token::{any, none_of, take, take_while},
+};
+
+use crate::json::JsonValue;
+
+pub type Stream<'i> = Partial<&'i str>;
+
+/// The root element of a JSON parser is any value
+///
+/// A parser has the following signature:
+/// `&mut Stream -> PResult<Output, InputError>`, with `PResult` defined as:
+/// `type PResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;`
+///
+/// most of the times you can ignore the error type and use the default (but this
+/// examples shows custom error types later on!)
+///
+/// Here we use `&str` as input type, but parsers can be generic over
+/// the input type, work directly with `&[u8]`, or any other type that
+/// implements the required traits.
+pub fn json<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ delimited(ws, json_value, ws_or_eof).parse_next(input)
+}
+
+/// `alt` is a combinator that tries multiple parsers one by one, until
+/// one of them succeeds
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ // `alt` combines the each value parser. It returns the result of the first
+ // successful parser, or an error
+ alt((
+ null.value(JsonValue::Null),
+ boolean.map(JsonValue::Boolean),
+ string.map(JsonValue::Str),
+ float.map(JsonValue::Num),
+ array.map(JsonValue::Array),
+ object.map(JsonValue::Object),
+ ))
+ .parse_next(input)
+}
+
+/// `tag(string)` generates a parser that recognizes the argument string.
+///
+/// This also shows returning a sub-slice of the original input
+fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // This is a parser that returns `"null"` if it sees the string "null", and
+ // an error otherwise
+ "null".parse_next(input)
+}
+
+/// We can combine `tag` with other functions, like `value` which returns a given constant value on
+/// success.
+fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> {
+ // This is a parser that returns `true` if it sees the string "true", and
+ // an error otherwise
+ let parse_true = "true".value(true);
+
+ // This is a parser that returns `false` if it sees the string "false", and
+ // an error otherwise
+ let parse_false = "false".value(false);
+
+ alt((parse_true, parse_false)).parse_next(input)
+}
+
+/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// character, before the string (using `preceded`) and after the string (using `terminated`).
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<String, E> {
+ preceded(
+ '\"',
+ // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to
+ // combinators like `alt` that they should not try other parsers. We were in the
+ // right branch (since we found the `"` character) but encountered an error when
+ // parsing the string
+ cut_err(terminated(
+ fold_repeat(0.., character, String::new, |mut string, c| {
+ string.push(c);
+ string
+ }),
+ '\"',
+ )),
+ )
+ // `context` lets you add a static string to errors to provide more information in the
+ // error chain (to indicate which parser had an error)
+ .context("string")
+ .parse_next(input)
+}
+
+/// You can mix the above declarative parsing with an imperative style to handle more unique cases,
+/// like escaping
+fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ let c = none_of('\"').parse_next(input)?;
+ if c == '\\' {
+ alt((
+ any.verify_map(|c| {
+ Some(match c {
+ '"' | '\\' | '/' => c,
+ 'b' => '\x08',
+ 'f' => '\x0C',
+ 'n' => '\n',
+ 'r' => '\r',
+ 't' => '\t',
+ _ => return None,
+ })
+ }),
+ preceded('u', unicode_escape),
+ ))
+ .parse_next(input)
+ } else {
+ Ok(c)
+ }
+}
+
+fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ alt((
+ // Not a surrogate
+ u16_hex
+ .verify(|cp| !(0xD800..0xE000).contains(cp))
+ .map(|cp| cp as u32),
+ // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details
+ separated_pair(u16_hex, "\\u", u16_hex)
+ .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low))
+ .map(|(high, low)| {
+ let high_ten = (high as u32) - 0xD800;
+ let low_ten = (low as u32) - 0xDC00;
+ (high_ten << 10) + low_ten + 0x10000
+ }),
+ ))
+ .verify_map(
+ // Could be probably replaced with .unwrap() or _unchecked due to the verify checks
+ std::char::from_u32,
+ )
+ .parse_next(input)
+}
+
+fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> {
+ take(4usize)
+ .verify_map(|s| u16::from_str_radix(s, 16).ok())
+ .parse_next(input)
+}
+
+/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly,
+/// accumulating results in a `Vec`, until it encounters an error.
+/// If you want more control on the parser application, check out the `iterator`
+/// combinator (cf `examples/iterator.rs`)
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<Vec<JsonValue>, E> {
+ preceded(
+ ('[', ws),
+ cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))),
+ )
+ .context("array")
+ .parse_next(input)
+}
+
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<HashMap<String, JsonValue>, E> {
+ preceded(
+ ('{', ws),
+ cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))),
+ )
+ .context("object")
+ .parse_next(input)
+}
+
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<(String, JsonValue), E> {
+ separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
+}
+
+/// Parser combinators are constructed from the bottom up:
+/// first we write parsers for the smallest elements (here a space character),
+/// then we'll combine them in larger parsers
+fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // Combinators like `take_while` return a function. That function is the
+ // parser,to which we can pass the input
+ take_while(0.., WS).parse_next(input)
+}
+
+fn ws_or_eof<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ rest.verify(|s: &str| s.chars().all(|c| WS.contains(&c)))
+ .parse_next(input)
+}
+
+const WS: &[char] = &[' ', '\t', '\r', '\n'];
+
+#[cfg(test)]
+mod test {
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ use super::*;
+
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ type Error<'i> = winnow::error::InputError<Partial<&'i str>>;
+
+ #[test]
+ fn json_string() {
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new("\"\"")),
+ Ok((Partial::new(""), "".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new("\"abc\"")),
+ Ok((Partial::new(""), "abc".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new(
+ "\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""
+ )),
+ Ok((
+ Partial::new(""),
+ "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string()
+ )),
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")),
+ Ok((Partial::new(""), "😐".to_string()))
+ );
+
+ assert!(string::<Error<'_>>.parse_peek(Partial::new("\"")).is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"abc"))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\u123\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\uD800\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\uD800\\uD800\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\uDC00\""))
+ .is_err());
+ }
+
+ #[test]
+ fn json_object() {
+ use JsonValue::{Num, Object, Str};
+
+ let input = r#"{"a":42,"b":"x"}"#;
+
+ let expected = Object(
+ vec![
+ ("a".to_string(), Num(42.0)),
+ ("b".to_string(), Str("x".to_string())),
+ ]
+ .into_iter()
+ .collect(),
+ );
+
+ assert_eq!(
+ json::<Error<'_>>.parse_peek(Partial::new(input)),
+ Ok((Partial::new(""), expected))
+ );
+ }
+
+ #[test]
+ fn json_array() {
+ use JsonValue::{Array, Num, Str};
+
+ let input = r#"[42,"x"]"#;
+
+ let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+
+ assert_eq!(
+ json::<Error<'_>>.parse_peek(Partial::new(input)),
+ Ok((Partial::new(""), expected))
+ );
+ }
+
+ #[test]
+ fn json_whitespace() {
+ use JsonValue::{Array, Boolean, Null, Num, Object, Str};
+
+ let input = r#"
+ {
+ "null" : null,
+ "true" :true ,
+ "false": false ,
+ "number" : 123e4 ,
+ "string" : " abc 123 " ,
+ "array" : [ false , 1 , "two" ] ,
+ "object" : { "a" : 1.0 , "b" : "c" } ,
+ "empty_array" : [ ] ,
+ "empty_object" : { }
+ }
+ "#;
+
+ assert_eq!(
+ json::<Error<'_>>.parse_peek(Partial::new(input)),
+ Ok((
+ Partial::new(""),
+ Object(
+ vec![
+ ("null".to_string(), Null),
+ ("true".to_string(), Boolean(true)),
+ ("false".to_string(), Boolean(false)),
+ ("number".to_string(), Num(123e4)),
+ ("string".to_string(), Str(" abc 123 ".to_string())),
+ (
+ "array".to_string(),
+ Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+ ),
+ (
+ "object".to_string(),
+ Object(
+ vec![
+ ("a".to_string(), Num(1.0)),
+ ("b".to_string(), Str("c".to_string())),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ),
+ ("empty_array".to_string(), Array(vec![]),),
+ ("empty_object".to_string(), Object(HashMap::new()),),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ))
+ );
+ }
+}
diff --git a/examples/json_iterator.rs b/examples/json_iterator.rs
new file mode 100644
index 0000000..b8b46f3
--- /dev/null
+++ b/examples/json_iterator.rs
@@ -0,0 +1,311 @@
+use std::collections::HashMap;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::{alphanumeric1 as alphanumeric, escaped, float},
+ combinator::alt,
+ combinator::cut_err,
+ combinator::separated0,
+ combinator::{preceded, separated_pair, terminated},
+ error::ParserError,
+ error::StrContext,
+ stream::Offset,
+ token::one_of,
+ token::{tag, take_while},
+};
+
+use std::cell::Cell;
+use std::str;
+
+#[derive(Clone, Debug)]
+pub struct JsonValue<'a, 'b> {
+ input: &'a str,
+ pub offset: &'b Cell<usize>,
+}
+
+impl<'a, 'b: 'a> JsonValue<'a, 'b> {
+ pub fn new(input: &'a str, offset: &'b Cell<usize>) -> JsonValue<'a, 'b> {
+ JsonValue { input, offset }
+ }
+
+ pub fn offset(&self, input: &'a str) {
+ let offset = input.offset_from(&self.input);
+ self.offset.set(offset);
+ }
+
+ pub fn data(&self) -> &'a str {
+ &self.input[self.offset.get()..]
+ }
+
+ pub fn string(&self) -> Option<&'a str> {
+ println!("string()");
+ let mut data = self.data();
+ match string(&mut data) {
+ Ok(s) => {
+ self.offset(data);
+ println!("-> {}", s);
+ Some(s)
+ }
+ _ => None,
+ }
+ }
+
+ pub fn boolean(&self) -> Option<bool> {
+ println!("boolean()");
+ let mut data = self.data();
+ match boolean(&mut data) {
+ Ok(o) => {
+ self.offset(data);
+ println!("-> {}", o);
+ Some(o)
+ }
+ _ => None,
+ }
+ }
+
+ pub fn number(&self) -> Option<f64> {
+ println!("number()");
+ let mut data = self.data();
+ match float::<_, _, ()>.parse_next(&mut data) {
+ Ok(o) => {
+ self.offset(data);
+ println!("-> {}", o);
+ Some(o)
+ }
+ _ => None,
+ }
+ }
+
+ pub fn array(&self) -> Option<impl Iterator<Item = JsonValue<'a, 'b>>> {
+ println!("array()");
+
+ let mut data = self.data();
+ match tag::<_, _, ()>("[").parse_next(&mut data) {
+ Err(_) => None,
+ Ok(_) => {
+ println!("[");
+ self.offset(data);
+ let mut first = true;
+ let mut done = false;
+ let mut previous = std::usize::MAX;
+
+ let v = self.clone();
+
+ Some(std::iter::from_fn(move || {
+ if done {
+ return None;
+ }
+
+ // if we ignored one of the items, skip over the value
+ if v.offset.get() == previous {
+ println!("skipping value");
+ if value(&mut data).is_ok() {
+ v.offset(data);
+ }
+ }
+
+ if tag::<_, _, ()>("]").parse_next(&mut data).is_ok() {
+ println!("]");
+ v.offset(data);
+ done = true;
+ return None;
+ }
+
+ if first {
+ first = false;
+ } else {
+ match tag::<_, _, ()>(",").parse_next(&mut data) {
+ Ok(_) => {
+ println!(",");
+ v.offset(data);
+ }
+ Err(_) => {
+ done = true;
+ return None;
+ }
+ }
+ }
+
+ println!("-> {}", v.data());
+ previous = v.offset.get();
+ Some(v.clone())
+ }))
+ }
+ }
+ }
+
+ pub fn object(&self) -> Option<impl Iterator<Item = (&'a str, JsonValue<'a, 'b>)>> {
+ println!("object()");
+ let mut data = self.data();
+ match tag::<_, _, ()>("{").parse_next(&mut data) {
+ Err(_) => None,
+ Ok(_) => {
+ self.offset(data);
+
+ println!("{{");
+
+ let mut first = true;
+ let mut done = false;
+ let mut previous = std::usize::MAX;
+
+ let v = self.clone();
+
+ Some(std::iter::from_fn(move || {
+ if done {
+ return None;
+ }
+
+ // if we ignored one of the items, skip over the value
+ if v.offset.get() == previous {
+ println!("skipping value");
+ if value(&mut data).is_ok() {
+ v.offset(data);
+ }
+ }
+
+ if tag::<_, _, ()>("}").parse_next(&mut data).is_ok() {
+ println!("}}");
+ v.offset(data);
+ done = true;
+ return None;
+ }
+
+ if first {
+ first = false;
+ } else {
+ match tag::<_, _, ()>(",").parse_next(&mut data) {
+ Ok(_) => {
+ println!(",");
+ v.offset(data);
+ }
+ Err(_) => {
+ done = true;
+ return None;
+ }
+ }
+ }
+
+ match string(&mut data) {
+ Ok(key) => {
+ v.offset(data);
+
+ match tag::<_, _, ()>(":").parse_next(&mut data) {
+ Err(_) => None,
+ Ok(_) => {
+ v.offset(data);
+
+ previous = v.offset.get();
+
+ println!("-> {} => {}", key, v.data());
+ Some((key, v.clone()))
+ }
+ }
+ }
+ _ => None,
+ }
+ }))
+ }
+ }
+ }
+}
+
+fn sp<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> {
+ let chars = " \t\r\n";
+
+ take_while(0.., move |c| chars.contains(c)).parse_next(i)
+}
+
+fn parse_str<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<&'a str, E> {
+ escaped(alphanumeric, '\\', one_of(['"', 'n', '\\'])).parse_next(i)
+}
+
+fn string<'s>(i: &mut &'s str) -> PResult<&'s str> {
+ preceded('\"', cut_err(terminated(parse_str, '\"')))
+ .context(StrContext::Label("string"))
+ .parse_next(i)
+}
+
+fn boolean(input: &mut &str) -> PResult<bool> {
+ alt(("false".map(|_| false), "true".map(|_| true))).parse_next(input)
+}
+
+fn array(i: &mut &str) -> PResult<()> {
+ preceded(
+ '[',
+ cut_err(terminated(
+ separated0(value, preceded(sp, ',')),
+ preceded(sp, ']'),
+ )),
+ )
+ .context(StrContext::Label("array"))
+ .parse_next(i)
+}
+
+fn key_value<'s>(i: &mut &'s str) -> PResult<(&'s str, ())> {
+ separated_pair(preceded(sp, string), cut_err(preceded(sp, ':')), value).parse_next(i)
+}
+
+fn hash(i: &mut &str) -> PResult<()> {
+ preceded(
+ '{',
+ cut_err(terminated(
+ separated0(key_value, preceded(sp, ',')),
+ preceded(sp, '}'),
+ )),
+ )
+ .context(StrContext::Label("map"))
+ .parse_next(i)
+}
+
+fn value(i: &mut &str) -> PResult<()> {
+ preceded(
+ sp,
+ alt((
+ hash,
+ array,
+ string.map(|_| ()),
+ float::<_, f64, _>.map(|_| ()),
+ boolean.map(|_| ()),
+ )),
+ )
+ .parse_next(i)
+}
+
+/// object(input) -> iterator over (key, `JsonValue`)
+/// array(input) -> iterator over `JsonValue`
+///
+/// JsonValue.string -> iterator over String (returns None after first successful call)
+///
+/// object(input).filter(|(k, _)| k == "users").flatten(|(_, v)| v.object()).filter(|(k, _)| k == "city").flatten(|(_,v)| v.string())
+fn main() {
+ /*let data = "{
+ \"users\": {
+ \"user1\" : { \"city\": \"Nantes\", \"country\": \"France\" },
+ \"user2\" : { \"city\": \"Bruxelles\", \"country\": \"Belgium\" },
+ \"user3\": { \"city\": \"Paris\", \"country\": \"France\", \"age\": 30 }
+ },
+ \"countries\": [\"France\", \"Belgium\"]
+ }";
+ */
+ let data = "{\"users\":{\"user1\":{\"city\":\"Nantes\",\"country\":\"France\"},\"user2\":{\"city\":\"Bruxelles\",\"country\":\"Belgium\"},\"user3\":{\"city\":\"Paris\",\"country\":\"France\",\"age\":30}},\"countries\":[\"France\",\"Belgium\"]}";
+
+ let offset = Cell::new(0);
+ {
+ let parser = JsonValue::new(data, &offset);
+
+ if let Some(o) = parser.object() {
+ let s: HashMap<&str, &str> = o
+ .filter(|(k, _)| *k == "users")
+ .filter_map(|(_, v)| v.object())
+ .flatten()
+ .filter_map(|(user, v)| v.object().map(|o| (user, o)))
+ .flat_map(|(user, o)| {
+ o.filter(|(k, _)| *k == "city")
+ .filter_map(move |(_, v)| v.string().map(|s| (user, s)))
+ })
+ .collect();
+
+ println!("res = {:?}", s);
+ }
+ };
+}
diff --git a/examples/ndjson/example.ndjson b/examples/ndjson/example.ndjson
new file mode 100644
index 0000000..7f1fca0
--- /dev/null
+++ b/examples/ndjson/example.ndjson
@@ -0,0 +1,158 @@
+{"reason":"compiler-artifact","package_id":"proc-macro2 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/proc-macro2-d6a7808ec27a845d/build-script-build"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"proc-macro2 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["use_proc_macro","wrap_proc_macro"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/proc-macro2-e500f83d0dabcc00/out"}
+{"reason":"compiler-artifact","package_id":"quote 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/quote-e70da9bace8e108a/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"libc 0.2.139 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","extra_traits","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/libc-ea536a8e67e0b7eb/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"unicode-ident 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-ident-1.0.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"unicode-ident","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-ident-1.0.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libunicode_ident-e72d3e3fa5fdcbf4.rlib","/home/epage/src/personal/winnow/target/debug/deps/libunicode_ident-e72d3e3fa5fdcbf4.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"proc-macro2 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"proc-macro2","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-1.0.46/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libproc_macro2-559e547b03a7ac1e.rlib","/home/epage/src/personal/winnow/target/debug/deps/libproc_macro2-559e547b03a7ac1e.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"libc 0.2.139 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["freebsd11","libc_priv_mod_use","libc_union","libc_const_size_of","libc_align","libc_int128","libc_core_cvoid","libc_packedN","libc_cfg_target_vendor","libc_non_exhaustive","libc_ptr_addr_of","libc_underscore_const_names","libc_const_extern_fn"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/libc-94d2f48bd38a8056/out"}
+{"reason":"build-script-executed","package_id":"quote 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/quote-a2754428d152a498/out"}
+{"reason":"compiler-artifact","package_id":"syn 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["clone-impls","default","derive","parsing","printing","proc-macro","quote"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/syn-c9e8af729632e4e4/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cfg-if-1.0.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"cfg-if","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cfg-if-1.0.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcfg_if-047a17fcf848a7e5.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"autocfg 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/autocfg-1.1.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"autocfg","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/autocfg-1.1.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libautocfg-25db3455927a66e1.rlib","/home/epage/src/personal/winnow/target/debug/deps/libautocfg-25db3455927a66e1.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde_derive 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/serde_derive-fcc2f4aec2a2d4ab/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","derive","serde_derive","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/serde-3f78a53b92e21d4d/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"libc 0.2.139 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"libc","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.139/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","extra_traits","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblibc-5b6dd9f3e6fc0120.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"quote 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quote","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","proc-macro"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquote-9a0c3a96b2cdc59c.rlib","/home/epage/src/personal/winnow/target/debug/deps/libquote-9a0c3a96b2cdc59c.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"syn 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["syn_disable_nightly_tests"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/syn-0a9a191063f1b2fc/out"}
+{"reason":"build-script-executed","package_id":"serde_derive 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/serde_derive-ead5e900bac8546f/out"}
+{"reason":"build-script-executed","package_id":"serde 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/serde-03f4af86861cbc3e/out"}
+{"reason":"compiler-artifact","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/thiserror-66462325c558a4d5/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"ryu 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ryu-1.0.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ryu","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ryu-1.0.11/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libryu-0e7b0d46c4589f15.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"crossbeam-utils 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/crossbeam-utils-c9170234d86239e2/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/memchr-99ffc1dfd2c517c1/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde_json 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/serde_json-3051866d1babe853/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"memoffset 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/memoffset-37767cb27e2441e6/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bitflags-1.3.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bitflags","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bitflags-1.3.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbitflags-ea9a4e086e887550.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"itoa 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-1.0.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-1.0.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libitoa-69c375d2fd4189a8.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"crossbeam-epoch 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/crossbeam-epoch-ec0452f91ac732bb/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"ucd-trie 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ucd-trie","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libucd_trie-eb38f7c85a03bb9d.rlib","/home/epage/src/personal/winnow/target/debug/deps/libucd_trie-eb38f7c85a03bb9d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"io-lifetimes 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["close","default","libc","windows-sys"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/io-lifetimes-18d168bedd0c64e8/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/log-52c513d099058ca0/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/num-traits-77d338745cc017c3/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rayon-core 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/rayon-core-eecac7c574986103/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rustix 0.36.8 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","io-lifetimes","libc","std","termios","use-libc-auxv"],"filenames":["/home/epage/src/personal/winnow/target/debug/build/rustix-32859c5d0061115d/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"syn 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"syn","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.102/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["clone-impls","default","derive","parsing","printing","proc-macro","quote"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsyn-c9741af862298610.rlib","/home/epage/src/personal/winnow/target/debug/deps/libsyn-c9741af862298610.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/thiserror-37d8ebe1b01cc51d/out"}
+{"reason":"build-script-executed","package_id":"memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["memchr_runtime_simd","memchr_runtime_sse2","memchr_runtime_sse42","memchr_runtime_avx"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/memchr-743d739a8480f48a/out"}
+{"reason":"build-script-executed","package_id":"crossbeam-utils 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/crossbeam-utils-1975bdb7ecfbff2a/out"}
+{"reason":"build-script-executed","package_id":"serde_json 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["limb_width_64"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/serde_json-3f569e9655a7cd7e/out"}
+{"reason":"build-script-executed","package_id":"memoffset 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["tuple_ty","allow_clippy","maybe_uninit","doctests","raw_ref_macros"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/memoffset-27100c1e8e709074/out"}
+{"reason":"build-script-executed","package_id":"crossbeam-epoch 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/crossbeam-epoch-7f643445aebfa633/out"}
+{"reason":"compiler-artifact","package_id":"getrandom 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.2.7/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"getrandom","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.2.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libgetrandom-d2efdd8bbd217458.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["atomic_cas","has_atomics"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/log-b56bc898d3207792/out"}
+{"reason":"build-script-executed","package_id":"io-lifetimes 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["io_safety_is_in_std","panic_in_const_fn"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/io-lifetimes-701560b8574ef205/out"}
+{"reason":"compiler-artifact","package_id":"once_cell 1.15.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"once_cell","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","race","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libonce_cell-c9f9ea925d35da52.rlib","/home/epage/src/personal/winnow/target/debug/deps/libonce_cell-c9f9ea925d35da52.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/scopeguard-1.1.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"scopeguard","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/scopeguard-1.1.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libscopeguard-13a4bbff1ce24cc7.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"rayon-core 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/rayon-core-b008d521ccec1e7b/out"}
+{"reason":"build-script-executed","package_id":"rustix 0.36.8 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["linux_raw","asm"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/rustix-e16304a596230d21/out"}
+{"reason":"build-script-executed","package_id":"num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["has_i128","has_to_int_unchecked","has_reverse_bits","has_leading_trailing_ones","has_int_assignop_ref","has_div_euclid","has_copysign"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/num-traits-6888d3d0832572a9/out"}
+{"reason":"compiler-artifact","package_id":"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"lazy_static","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lazy_static-1.4.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblazy_static-afa8b761bb5d6b57.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-width-0.1.10/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"unicode-width","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/unicode-width-0.1.10/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libunicode_width-8dcd31c030e77d42.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"either 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/either-1.8.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"either","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/either-1.8.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["use_std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libeither-3c87d508139ef632.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"linux-raw-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/linux-raw-sys-0.1.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"linux-raw-sys","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/linux-raw-sys-0.1.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["errno","general","ioctl","no_std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblinux_raw_sys-297593f8ceab708f.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"num_cpus 1.13.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num_cpus-1.13.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"num_cpus","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num_cpus-1.13.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libnum_cpus-539d2b6f1744794b.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde_derive 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"serde_derive","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_derive-1.0.145/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde_derive-7934298a61c1a1a2.so"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"thiserror-impl 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-impl-1.0.38/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"thiserror-impl","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-impl-1.0.38/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libthiserror_impl-213d86e6d4b69b5f.so"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"memchr","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memchr-2.5.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libmemchr-ae9722f3894e3314.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"crossbeam-utils 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-utils","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.12/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_utils-59f0a08b7eefe073.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"memoffset 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"memoffset","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/memoffset-0.6.5/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libmemoffset-4787845978faa8b8.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"io-lifetimes 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"io-lifetimes","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/io-lifetimes-1.0.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["close","default","libc","windows-sys"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libio_lifetimes-0d158e24024d572c.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"log","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/log-0.4.17/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblog-3242a8c3b3d72769.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.6.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand_core","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.6.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","getrandom","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand_core-aa1df72cb81e420e.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"num-traits 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"num-traits","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/num-traits-0.2.15/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libnum_traits-1c8d0251ac4ea61d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rayon 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/rayon-4f0513187044c0f2/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"escargot 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/build.rs","edition":"2018","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/escargot-44a89e78cfd0d26f/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"yansi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/yansi-0.5.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"yansi","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/yansi-0.5.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libyansi-62809433fc3ff8e9.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-automata-0.1.10/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"regex-automata","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-automata-0.1.10/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libregex_automata-741e79c4b6938d7d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/remove_dir_all-0.5.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"remove_dir_all","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/remove_dir_all-0.5.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libremove_dir_all-bcafaf4f00d8e4a4.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"regex-syntax 0.6.27 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-syntax-0.6.27/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"regex-syntax","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-syntax-0.6.27/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","unicode","unicode-age","unicode-bool","unicode-case","unicode-gencat","unicode-perl","unicode-script","unicode-segment"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libregex_syntax-3fe0a07eb00f644f.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"ucd-trie 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ucd-trie","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ucd-trie-0.1.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libucd_trie-b01a07ea40c7e7ed.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"plotters-backend 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-backend-0.3.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"plotters-backend","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-backend-0.3.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libplotters_backend-34c8fa8c2b121eeb.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"ppv-lite86 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ppv-lite86-0.2.16/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"ppv-lite86","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/ppv-lite86-0.2.16/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["simd","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libppv_lite86-3b9c603c4b32aff6.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"fastrand 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fastrand-1.8.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"fastrand","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fastrand-1.8.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libfastrand-52ad6b37c39c8a90.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"itertools 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itertools","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itertools-0.10.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","use_alloc","use_std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libitertools-ba05d1064477b941.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde 1.0.145 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.145/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","derive","serde_derive","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde-e026f194fb97de03.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"thiserror","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libthiserror-4481f6326f711f93.rlib","/home/epage/src/personal/winnow/target/debug/deps/libthiserror-4481f6326f711f93.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"crossbeam-epoch 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-epoch","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-epoch-0.9.11/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_epoch-caa786e06425cee9.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"thiserror 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"thiserror","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/thiserror-1.0.38/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libthiserror-9187d79370422188.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rustix 0.36.8 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rustix","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rustix-0.36.8/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","io-lifetimes","libc","std","termios","use-libc-auxv"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librustix-ec48622574cca747.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"crossbeam-channel 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-channel-0.5.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-channel","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-channel-0.5.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["crossbeam-utils","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_channel-a9b11ccef7773884.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"escargot 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/escargot-5711ac23b4781245/out"}
+{"reason":"compiler-artifact","package_id":"tempfile 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tempfile-3.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"tempfile","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tempfile-3.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtempfile-8b83de608c2a0658.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rand_chacha 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_chacha-0.3.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand_chacha","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_chacha-0.3.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand_chacha-5967d0068d39b018.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"rayon 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":["has_step_by_rev","has_min_const_generics","has_control_flow"],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/rayon-d01aa17f59a1e98f/out"}
+{"reason":"compiler-artifact","package_id":"plotters-svg 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-svg-0.3.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"plotters-svg","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-svg-0.3.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libplotters_svg-2bb014e4a5d863b1.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-core-0.1.10/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"csv-core","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-core-0.1.10/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcsv_core-bf47a6ecf716f212.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"textwrap","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/textwrap-0.11.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtextwrap-1f9a5b633ba2872d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/atty-0.2.14/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"atty","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/atty-0.2.14/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libatty-7590cc9da5872c3b.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/wait-timeout-0.2.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"wait-timeout","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/wait-timeout-0.2.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libwait_timeout-d79299ed1c2df76b.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"diff 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/diff-0.1.13/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"diff","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/diff-0.1.13/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libdiff-a3c1327ada950886.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"concolor-query 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-query-0.1.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"concolor-query","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-query-0.1.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["windows"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libconcolor_query-1b1f33bda6da9636.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/same-file-1.0.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"same-file","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/same-file-1.0.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsame_file-8dba8c6e6bbcef0a.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"bit-vec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-vec-0.6.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bit-vec","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-vec-0.6.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbit_vec-5cc1be6dbbb7d172.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fnv-1.0.7/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"fnv","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/fnv-1.0.7/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libfnv-e811c8615aede027.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde_json 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde_json","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.86/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde_json-10d232e85191c379.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"pest 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std","thiserror"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest-c28124a2ace8ce41.rlib","/home/epage/src/personal/winnow/target/debug/deps/libpest-c28124a2ace8ce41.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"crossbeam-deque 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-deque-0.8.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"crossbeam-deque","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-deque-0.8.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["crossbeam-epoch","crossbeam-utils","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcrossbeam_deque-a531937c1e514cf3.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"bstr 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bstr-0.2.17/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bstr","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bstr-0.2.17/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","lazy_static","regex-automata","serde","serde1","serde1-nostd","std","unicode"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbstr-3245cbc7e1b4eee0.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"is-terminal 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/is-terminal-0.4.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"is-terminal","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/is-terminal-0.4.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libis_terminal-ddb4d6f1cea88415.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"pest 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std","thiserror"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest-3fe2406158c36e91.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/Cargo.toml","target":{"kind":["custom-build"],"crate_types":["bin"],"name":"build-script-build","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/build.rs","edition":"2015","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/build/doc-comment-476a3be5ae9523a0/build-script-build"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"cast 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cast-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"cast","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/cast-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcast-0f6ca0f808d89c22.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"half 1.8.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/half-1.8.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"half","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/half-1.8.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libhalf-ec0164f76c2e0030.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-1.2.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quick-error","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-1.2.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquick_error-e7675fcf1fdb276d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"once_cell 1.15.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"once_cell","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/once_cell-1.15.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","race","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libonce_cell-6a83ca81f3790104.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"itoa 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-0.4.8/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"itoa","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/itoa-0.4.8/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libitoa-af0357b7d39004b0.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"plotters 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-0.3.4/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"plotters","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-0.3.4/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["area_series","line_series","plotters-svg","svg_backend"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libplotters-c6ddec411d8166e5.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"walkdir 2.3.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/walkdir-2.3.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"walkdir","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/walkdir-2.3.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libwalkdir-9e7e41a0b2bf038d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"bit-set 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-set-0.5.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bit-set","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bit-set-0.5.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbit_set-2aef4909cbe7f4dc.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"pretty_assertions 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pretty_assertions-1.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pretty_assertions","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pretty_assertions-1.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpretty_assertions-917f731810bb748b.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-2.34.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"clap","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/clap-2.34.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libclap-af397f0f338fc8b9.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand-0.8.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","getrandom","libc","rand_chacha","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand-79231f927b479fcb.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"quick-xml 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-xml-0.23.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quick-xml","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-xml-0.23.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquick_xml-56f1f63175d80ec8.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rand_xorshift 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_xorshift-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rand_xorshift","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_xorshift-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librand_xorshift-52cda07a05fef0b5.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"pest_meta 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_meta-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest_meta","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_meta-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest_meta-e592b8a19ab895e0.rlib","/home/epage/src/personal/winnow/target/debug/deps/libpest_meta-e592b8a19ab895e0.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rayon-core 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rayon-core","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-core-1.9.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librayon_core-28c7fd349d6eb8a3.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"tinytemplate 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tinytemplate-1.2.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"tinytemplate","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/tinytemplate-1.2.1/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtinytemplate-1e34f9b3f54388b1.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"serde_cbor 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_cbor-0.11.2/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"serde_cbor","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_cbor-0.11.2/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libserde_cbor-a6696a57f0dc1b3e.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"escargot 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"escargot","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/escargot-0.5.7/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libescargot-8a922c953f0ce0d3.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"csv 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-1.1.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"csv","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/csv-1.1.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcsv-66898c2026240d6e.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rusty-fork 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rusty-fork-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rusty-fork","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rusty-fork-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["timeout","wait-timeout"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librusty_fork-3ff190906b9b0c88.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-script-executed","package_id":"doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","linked_libs":[],"linked_paths":[],"cfgs":[],"env":[],"out_dir":"/home/epage/src/personal/winnow/target/debug/build/doc-comment-9f27b6aeba0913e7/out"}
+{"reason":"compiler-artifact","package_id":"criterion-plot 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-plot-0.4.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"criterion-plot","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-plot-0.4.5/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcriterion_plot-dc7b9242779f53d6.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"concolor 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-0.0.11/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"concolor","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/concolor-0.0.11/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["auto","bitflags","clicolor","concolor-query","core","interactive","no_color","std","term","windows"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libconcolor-c2d97e9aa66666d7.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"regex 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-1.6.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"regex","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-1.6.0/src/lib.rs","edition":"2018","doc":true,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libregex-14a9fe50552aac49.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"os_pipe 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/os_pipe-1.1.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"os_pipe","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/os_pipe-1.1.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libos_pipe-be2a22288f932a49.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"quick-error 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-2.0.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"quick-error","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/quick-error-2.0.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libquick_error-280edfc32cc83812.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"normalize-line-endings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/normalize-line-endings-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"normalize-line-endings","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/normalize-line-endings-0.3.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libnormalize_line_endings-8a66ba357a389996.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bytecount-0.6.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"bytecount","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/bytecount-0.6.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbytecount-c083e2cdbbb4f003.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"snapbox-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-macros-0.3.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"snapbox-macros","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-macros-0.3.1/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsnapbox_macros-d9da77d55f4279bc.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"oorandom 11.1.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/oorandom-11.1.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"oorandom","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/oorandom-11.1.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liboorandom-052fb0be6ac16c94.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"byteorder 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"byteorder","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libbyteorder-9f6a3ecb302657b0.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"similar 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/similar-2.2.1/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"similar","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/similar-2.2.1/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","inline","text"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsimilar-ca2082771c207a3c.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/termcolor-1.2.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"termcolor","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/termcolor-1.2.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libtermcolor-dad1a04bc5d2f742.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"pest_generator 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_generator-2.5.5/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"pest_generator","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_generator-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest_generator-17d62a3fe28e46f5.rlib","/home/epage/src/personal/winnow/target/debug/deps/libpest_generator-17d62a3fe28e46f5.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"rayon 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"rayon","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/rayon-1.5.3/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/librayon-41dd0a10f9292557.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"proptest 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proptest-1.0.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"proptest","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/proptest-1.0.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["bit-set","break-dead-code","default","fork","lazy_static","quick-error","regex-syntax","rusty-fork","std","tempfile","timeout"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libproptest-7f94966e4936da95.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"snapbox 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-0.4.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"snapbox","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/snapbox-0.4.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["color","color-auto","concolor","default","diff","examples"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libsnapbox-d99468a5201f9b87.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"doc_comment","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/doc-comment-0.3.3/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libdoc_comment-5644793091a6953d.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"lexopt 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lexopt-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"lexopt","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/lexopt-0.3.0/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/liblexopt-c945e030a97b8e1f.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"circular 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/circular-0.3.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"circular","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/circular-0.3.0/src/lib.rs","edition":"2015","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcircular-6190d46717d189a0.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"winnow","src_path":"/home/epage/src/personal/winnow/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libwinnow-a64b99fd45b2e97c.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"pest_derive 2.5.5 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_derive-2.5.5/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"pest_derive","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/pest_derive-2.5.5/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libpest_derive-23acec6b80f586aa.so"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"criterion 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-0.3.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"criterion","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/criterion-0.3.6/src/lib.rs","edition":"2018","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["cargo_bench_support","default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libcriterion-864d2d30e85a25cf.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"handlebars 4.3.6 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-4.3.6/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"handlebars","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-4.3.6/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["default"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libhandlebars-8f7ca769e2915c7a.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"term-transcript 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)","manifest_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/term-transcript-0.2.0/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"term-transcript","src_path":"/home/epage/.cargo/registry/src/github.com-1ecc6299db9ec823/term-transcript-0.2.0/src/lib.rs","edition":"2021","doc":true,"doctest":true,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["atty","default","handlebars","pretty_assertions","quick-xml","serde","svg","test"],"filenames":["/home/epage/src/personal/winnow/target/debug/deps/libterm_transcript-67537cf74f0568cc.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"ini","src_path":"/home/epage/src/personal/winnow/examples/ini/main.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libini-029ff669d11d054a.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"custom_error","src_path":"/home/epage/src/personal/winnow/examples/custom_error.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libcustom_error-8382fb7d49937909.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"s_expression","src_path":"/home/epage/src/personal/winnow/examples/s_expression/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libs_expression-0161e16eb3795e0c.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"css","src_path":"/home/epage/src/personal/winnow/examples/css/main.rs","edition":"2021","doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libcss-d003331b0eaf24a5.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"json","src_path":"/home/epage/src/personal/winnow/examples/json/main.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libjson-20da0c01e9db35aa.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"ndjson","src_path":"/home/epage/src/personal/winnow/examples/ndjson/main.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libndjson-17afbe6b158251ab.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"http","src_path":"/home/epage/src/personal/winnow/examples/http/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libhttp-08613cd431f59551.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"iterator","src_path":"/home/epage/src/personal/winnow/examples/iterator.rs","edition":"2021","doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libiterator-46a33f71a5378497.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"string","src_path":"/home/epage/src/personal/winnow/examples/string/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libstring-73fadc9999eff689.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"arithmetic","src_path":"/home/epage/src/personal/winnow/examples/arithmetic/main.rs","edition":"2021","required-features":["alloc"],"doc":false,"doctest":false,"test":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libarithmetic-04ca5fb45c28ebc2.rmeta"],"executable":null,"fresh":true}
+{"reason":"compiler-artifact","package_id":"winnow 0.3.1 (path+file:///home/epage/src/personal/winnow)","manifest_path":"/home/epage/src/personal/winnow/Cargo.toml","target":{"kind":["example"],"crate_types":["bin"],"name":"json_iterator","src_path":"/home/epage/src/personal/winnow/examples/json_iterator.rs","edition":"2021","required-features":["std"],"doc":false,"doctest":false,"test":false},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":["alloc","default","std"],"filenames":["/home/epage/src/personal/winnow/target/debug/examples/libjson_iterator-9bb330a957b0d53d.rmeta"],"executable":null,"fresh":true}
+{"reason":"build-finished","success":true}
+
diff --git a/examples/ndjson/main.rs b/examples/ndjson/main.rs
new file mode 100644
index 0000000..6b2a716
--- /dev/null
+++ b/examples/ndjson/main.rs
@@ -0,0 +1,114 @@
+mod parser;
+
+use std::io::Read;
+
+use winnow::error::ErrMode;
+use winnow::error::InputError;
+use winnow::error::Needed;
+use winnow::prelude::*;
+use winnow::stream::Offset;
+
+fn main() -> Result<(), lexopt::Error> {
+ let args = Args::parse()?;
+ let input = args.input.ok_or_else(|| lexopt::Error::MissingValue {
+ option: Some("<PATH>".to_owned()),
+ })?;
+
+ let mut file = std::fs::File::open(&input).map_err(to_lexopt)?;
+
+ // Intentionally starting with a small buffer to make it easier to show `Incomplete` handling
+ let buffer_size = 10;
+ let min_buffer_growth = 100;
+ let buffer_growth_factor = 2;
+ let mut buffer = circular::Buffer::with_capacity(buffer_size);
+ loop {
+ let read = file.read(buffer.space()).map_err(to_lexopt)?;
+ eprintln!("read {}", read);
+ if read == 0 {
+ // Should be EOF since we always make sure there is `available_space`
+ assert_ne!(buffer.available_space(), 0);
+ assert_eq!(
+ buffer.available_data(),
+ 0,
+ "leftover data: {}",
+ String::from_utf8_lossy(buffer.data())
+ );
+ break;
+ }
+ buffer.fill(read);
+
+ loop {
+ let input = parser::Stream::new(std::str::from_utf8(buffer.data()).map_err(to_lexopt)?);
+ match parser::ndjson::<InputError<parser::Stream>>.parse_peek(input) {
+ Ok((remainder, value)) => {
+ println!("{:?}", value);
+ println!();
+ // Tell the buffer how much we read
+ let consumed = remainder.offset_from(&input);
+ buffer.consume(consumed);
+ }
+ Err(ErrMode::Backtrack(e)) | Err(ErrMode::Cut(e)) => {
+ return Err(fmt_lexopt(e.to_string()));
+ }
+ Err(ErrMode::Incomplete(Needed::Size(size))) => {
+ // Without the format telling us how much space is required, we really should
+ // treat this the same as `Unknown` but are doing this to demonstrate how to
+ // handle `Size`.
+ //
+ // Even when the format has a header to tell us `Size`, we could hit incidental
+ // `Size(1)`s, so make sure we buffer more space than that to avoid reading
+ // one byte at a time
+ let head_room = size.get().max(min_buffer_growth);
+ let new_capacity = buffer.available_data() + head_room;
+ eprintln!("growing buffer to {}", new_capacity);
+ buffer.grow(new_capacity);
+ if buffer.available_space() < head_room {
+ eprintln!("buffer shift");
+ buffer.shift();
+ }
+ break;
+ }
+ Err(ErrMode::Incomplete(Needed::Unknown)) => {
+ let new_capacity = buffer_growth_factor * buffer.capacity();
+ eprintln!("growing buffer to {}", new_capacity);
+ buffer.grow(new_capacity);
+ break;
+ }
+ }
+ }
+ }
+
+ Ok(())
+}
+
+#[derive(Default)]
+struct Args {
+ input: Option<std::path::PathBuf>,
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Value(input) => {
+ res.input = Some(input.into());
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
+
+fn to_lexopt(e: impl std::error::Error + Send + Sync + 'static) -> lexopt::Error {
+ lexopt::Error::Custom(Box::new(e))
+}
+
+fn fmt_lexopt(e: String) -> lexopt::Error {
+ lexopt::Error::Custom(e.into())
+}
diff --git a/examples/ndjson/parser.rs b/examples/ndjson/parser.rs
new file mode 100644
index 0000000..aaa5c93
--- /dev/null
+++ b/examples/ndjson/parser.rs
@@ -0,0 +1,338 @@
+use std::collections::HashMap;
+use std::str;
+
+use winnow::prelude::*;
+use winnow::{
+ ascii::float,
+ ascii::line_ending,
+ combinator::alt,
+ combinator::cut_err,
+ combinator::{delimited, preceded, separated_pair, terminated},
+ combinator::{fold_repeat, separated0},
+ error::{AddContext, ParserError},
+ stream::Partial,
+ token::{any, none_of, take, take_while},
+};
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum JsonValue {
+ Null,
+ Boolean(bool),
+ Str(String),
+ Num(f64),
+ Array(Vec<JsonValue>),
+ Object(HashMap<String, JsonValue>),
+}
+
+/// Use `Partial` to cause `ErrMode::Incomplete` while parsing
+pub type Stream<'i> = Partial<&'i str>;
+
+pub fn ndjson<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<Option<JsonValue>, E> {
+ alt((
+ terminated(delimited(ws, json_value, ws), line_ending).map(Some),
+ line_ending.value(None),
+ ))
+ .parse_next(input)
+}
+
+// --Besides `WS`, same as a regular json parser ----------------------------
+
+/// `alt` is a combinator that tries multiple parsers one by one, until
+/// one of them succeeds
+fn json_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<JsonValue, E> {
+ // `alt` combines the each value parser. It returns the result of the first
+ // successful parser, or an error
+ alt((
+ null.value(JsonValue::Null),
+ boolean.map(JsonValue::Boolean),
+ string.map(JsonValue::Str),
+ float.map(JsonValue::Num),
+ array.map(JsonValue::Array),
+ object.map(JsonValue::Object),
+ ))
+ .parse_next(input)
+}
+
+/// `tag(string)` generates a parser that recognizes the argument string.
+///
+/// This also shows returning a sub-slice of the original input
+fn null<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // This is a parser that returns `"null"` if it sees the string "null", and
+ // an error otherwise
+ "null".parse_next(input)
+}
+
+/// We can combine `tag` with other functions, like `value` which returns a given constant value on
+/// success.
+fn boolean<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<bool, E> {
+ // This is a parser that returns `true` if it sees the string "true", and
+ // an error otherwise
+ let parse_true = "true".value(true);
+
+ // This is a parser that returns `false` if it sees the string "false", and
+ // an error otherwise
+ let parse_false = "false".value(false);
+
+ alt((parse_true, parse_false)).parse_next(input)
+}
+
+/// This parser gathers all `char`s up into a `String`with a parse to recognize the double quote
+/// character, before the string (using `preceded`) and after the string (using `terminated`).
+fn string<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<String, E> {
+ preceded(
+ '\"',
+ // `cut_err` transforms an `ErrMode::Backtrack(e)` to `ErrMode::Cut(e)`, signaling to
+ // combinators like `alt` that they should not try other parsers. We were in the
+ // right branch (since we found the `"` character) but encountered an error when
+ // parsing the string
+ cut_err(terminated(
+ fold_repeat(0.., character, String::new, |mut string, c| {
+ string.push(c);
+ string
+ }),
+ '\"',
+ )),
+ )
+ // `context` lets you add a static string to errors to provide more information in the
+ // error chain (to indicate which parser had an error)
+ .context("string")
+ .parse_next(input)
+}
+
+/// You can mix the above declarative parsing with an imperative style to handle more unique cases,
+/// like escaping
+fn character<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ let c = none_of('"').parse_next(input)?;
+ if c == '\\' {
+ alt((
+ any.verify_map(|c| {
+ Some(match c {
+ '"' | '\\' | '/' => c,
+ 'b' => '\x08',
+ 'f' => '\x0C',
+ 'n' => '\n',
+ 'r' => '\r',
+ 't' => '\t',
+ _ => return None,
+ })
+ }),
+ preceded('u', unicode_escape),
+ ))
+ .parse_next(input)
+ } else {
+ Ok(c)
+ }
+}
+
+fn unicode_escape<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<char, E> {
+ alt((
+ // Not a surrogate
+ u16_hex
+ .verify(|cp| !(0xD800..0xE000).contains(cp))
+ .map(|cp| cp as u32),
+ // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details
+ separated_pair(u16_hex, "\\u", u16_hex)
+ .verify(|(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low))
+ .map(|(high, low)| {
+ let high_ten = (high as u32) - 0xD800;
+ let low_ten = (low as u32) - 0xDC00;
+ (high_ten << 10) + low_ten + 0x10000
+ }),
+ ))
+ .verify_map(
+ // Could be probably replaced with .unwrap() or _unchecked due to the verify checks
+ std::char::from_u32,
+ )
+ .parse_next(input)
+}
+
+fn u16_hex<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<u16, E> {
+ take(4usize)
+ .verify_map(|s| u16::from_str_radix(s, 16).ok())
+ .parse_next(input)
+}
+
+/// Some combinators, like `separated0` or `many0`, will call a parser repeatedly,
+/// accumulating results in a `Vec`, until it encounters an error.
+/// If you want more control on the parser application, check out the `iterator`
+/// combinator (cf `examples/iterator.rs`)
+fn array<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<Vec<JsonValue>, E> {
+ preceded(
+ ('[', ws),
+ cut_err(terminated(separated0(json_value, (ws, ',', ws)), (ws, ']'))),
+ )
+ .context("array")
+ .parse_next(input)
+}
+
+fn object<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<HashMap<String, JsonValue>, E> {
+ preceded(
+ ('{', ws),
+ cut_err(terminated(separated0(key_value, (ws, ',', ws)), (ws, '}'))),
+ )
+ .context("object")
+ .parse_next(input)
+}
+
+fn key_value<'i, E: ParserError<Stream<'i>> + AddContext<Stream<'i>, &'static str>>(
+ input: &mut Stream<'i>,
+) -> PResult<(String, JsonValue), E> {
+ separated_pair(string, cut_err((ws, ':', ws)), json_value).parse_next(input)
+}
+
+/// Parser combinators are constructed from the bottom up:
+/// first we write parsers for the smallest elements (here a space character),
+/// then we'll combine them in larger parsers
+fn ws<'i, E: ParserError<Stream<'i>>>(input: &mut Stream<'i>) -> PResult<&'i str, E> {
+ // Combinators like `take_while` return a function. That function is the
+ // parser,to which we can pass the input
+ take_while(0.., WS).parse_next(input)
+}
+
+const WS: &[char] = &[' ', '\t'];
+
+#[cfg(test)]
+mod test {
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ use super::*;
+
+ #[allow(clippy::useless_attribute)]
+ #[allow(dead_code)] // its dead for benches
+ type Error<'i> = winnow::error::InputError<Partial<&'i str>>;
+
+ #[test]
+ fn json_string() {
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new("\"\"")),
+ Ok((Partial::new(""), "".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new("\"abc\"")),
+ Ok((Partial::new(""), "abc".to_string()))
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new(
+ "\"abc\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0001\\u2014\u{2014}def\""
+ )),
+ Ok((
+ Partial::new(""),
+ "abc\"\\/\x08\x0C\n\r\t\x01——def".to_string()
+ )),
+ );
+ assert_eq!(
+ string::<Error<'_>>.parse_peek(Partial::new("\"\\uD83D\\uDE10\"")),
+ Ok((Partial::new(""), "😐".to_string()))
+ );
+
+ assert!(string::<Error<'_>>.parse_peek(Partial::new("\"")).is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"abc"))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\u123\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\uD800\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\uD800\\uD800\""))
+ .is_err());
+ assert!(string::<Error<'_>>
+ .parse_peek(Partial::new("\"\\uDC00\""))
+ .is_err());
+ }
+
+ #[test]
+ fn json_object() {
+ use JsonValue::{Num, Object, Str};
+
+ let input = r#"{"a":42,"b":"x"}
+"#;
+
+ let expected = Object(
+ vec![
+ ("a".to_string(), Num(42.0)),
+ ("b".to_string(), Str("x".to_string())),
+ ]
+ .into_iter()
+ .collect(),
+ );
+
+ assert_eq!(
+ ndjson::<Error<'_>>.parse_peek(Partial::new(input)),
+ Ok((Partial::new(""), Some(expected)))
+ );
+ }
+
+ #[test]
+ fn json_array() {
+ use JsonValue::{Array, Num, Str};
+
+ let input = r#"[42,"x"]
+"#;
+
+ let expected = Array(vec![Num(42.0), Str("x".to_string())]);
+
+ assert_eq!(
+ ndjson::<Error<'_>>.parse_peek(Partial::new(input)),
+ Ok((Partial::new(""), Some(expected)))
+ );
+ }
+
+ #[test]
+ fn json_whitespace() {
+ use JsonValue::{Array, Boolean, Null, Num, Object, Str};
+
+ let input = r#" { "null" : null, "true" :true , "false": false , "number" : 123e4 , "string" : " abc 123 " , "array" : [ false , 1 , "two" ] , "object" : { "a" : 1.0 , "b" : "c" } , "empty_array" : [ ] , "empty_object" : { } }
+"#;
+
+ assert_eq!(
+ ndjson::<Error<'_>>.parse_peek(Partial::new(input)),
+ Ok((
+ Partial::new(""),
+ Some(Object(
+ vec![
+ ("null".to_string(), Null),
+ ("true".to_string(), Boolean(true)),
+ ("false".to_string(), Boolean(false)),
+ ("number".to_string(), Num(123e4)),
+ ("string".to_string(), Str(" abc 123 ".to_string())),
+ (
+ "array".to_string(),
+ Array(vec![Boolean(false), Num(1.0), Str("two".to_string())])
+ ),
+ (
+ "object".to_string(),
+ Object(
+ vec![
+ ("a".to_string(), Num(1.0)),
+ ("b".to_string(), Str("c".to_string())),
+ ]
+ .into_iter()
+ .collect()
+ )
+ ),
+ ("empty_array".to_string(), Array(vec![]),),
+ ("empty_object".to_string(), Object(HashMap::new()),),
+ ]
+ .into_iter()
+ .collect()
+ ))
+ ))
+ );
+ }
+}
diff --git a/examples/s_expression/main.rs b/examples/s_expression/main.rs
new file mode 100644
index 0000000..da055be
--- /dev/null
+++ b/examples/s_expression/main.rs
@@ -0,0 +1,20 @@
+//! In this example we build an [S-expression](https://en.wikipedia.org/wiki/S-expression)
+//! parser and tiny [lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)) interpreter.
+//! Lisp is a simple type of language made up of Atoms and Lists, forming easily parsable trees.
+
+#![cfg(feature = "alloc")]
+
+mod parser;
+
+fn main() {
+ let expression_1 = "((if (= (+ 3 (/ 9 3))
+ (* 2 3))
+ *
+ /)
+ 456 123)";
+ println!(
+ "\"{}\"\nevaled gives us: {:?}",
+ expression_1,
+ parser::eval_from_str(expression_1)
+ );
+}
diff --git a/examples/s_expression/parser.rs b/examples/s_expression/parser.rs
new file mode 100644
index 0000000..919dcf4
--- /dev/null
+++ b/examples/s_expression/parser.rs
@@ -0,0 +1,361 @@
+//! In this example we build an [S-expression](https://en.wikipedia.org/wiki/S-expression)
+//! parser and tiny [lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)) interpreter.
+//! Lisp is a simple type of language made up of Atoms and Lists, forming easily parsable trees.
+
+use winnow::{
+ ascii::{alpha1, digit1, multispace0, multispace1},
+ combinator::alt,
+ combinator::repeat,
+ combinator::{cut_err, opt},
+ combinator::{delimited, preceded, terminated},
+ error::ContextError,
+ error::StrContext,
+ prelude::*,
+ token::one_of,
+};
+
+/// We start with a top-level function to tie everything together, letting
+/// us call eval on a string directly
+pub fn eval_from_str(src: &str) -> Result<Expr, String> {
+ parse_expr
+ .parse(src)
+ .map_err(|e| e.to_string())
+ .and_then(|exp| eval_expression(exp).ok_or_else(|| "Eval failed".to_string()))
+}
+
+/// For parsing, we start by defining the types that define the shape of data that we want.
+/// In this case, we want something tree-like
+
+/// The remaining half is Lists. We implement these as recursive Expressions.
+/// For a list of numbers, we have `'(1 2 3)`, which we'll parse to:
+/// ```
+/// Expr::Quote(vec![Expr::Constant(Atom::Num(1)),
+/// Expr::Constant(Atom::Num(2)),
+/// Expr::Constant(Atom::Num(3))])
+/// Quote takes an S-expression and prevents evaluation of it, making it a data
+/// structure that we can deal with programmatically. Thus any valid expression
+/// is also a valid data structure in Lisp itself.
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum Expr {
+ Constant(Atom),
+ /// (func-name arg1 arg2)
+ Application(Box<Expr>, Vec<Expr>),
+ /// (if predicate do-this)
+ If(Box<Expr>, Box<Expr>),
+ /// (if predicate do-this otherwise-do-this)
+ IfElse(Box<Expr>, Box<Expr>, Box<Expr>),
+ /// '(3 (if (+ 3 3) 4 5) 7)
+ Quote(Vec<Expr>),
+}
+
+/// We now wrap this type and a few other primitives into our Atom type.
+/// Remember from before that Atoms form one half of our language.
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum Atom {
+ Num(i32),
+ Keyword(String),
+ Boolean(bool),
+ BuiltIn(BuiltIn),
+}
+
+/// Now, the most basic type. We define some built-in functions that our lisp has
+#[derive(Debug, Eq, PartialEq, Clone, Copy)]
+pub enum BuiltIn {
+ Plus,
+ Minus,
+ Times,
+ Divide,
+ Equal,
+ Not,
+}
+
+/// With types defined, we move onto the top-level expression parser!
+fn parse_expr(i: &mut &'_ str) -> PResult<Expr> {
+ preceded(
+ multispace0,
+ alt((parse_constant, parse_application, parse_if, parse_quote)),
+ )
+ .parse_next(i)
+}
+
+/// We then add the Expr layer on top
+fn parse_constant(i: &mut &'_ str) -> PResult<Expr> {
+ parse_atom.map(Expr::Constant).parse_next(i)
+}
+
+/// Now we take all these simple parsers and connect them.
+/// We can now parse half of our language!
+fn parse_atom(i: &mut &'_ str) -> PResult<Atom> {
+ alt((
+ parse_num,
+ parse_bool,
+ parse_builtin.map(Atom::BuiltIn),
+ parse_keyword,
+ ))
+ .parse_next(i)
+}
+
+/// Next up is number parsing. We're keeping it simple here by accepting any number (> 1)
+/// of digits but ending the program if it doesn't fit into an i32.
+fn parse_num(i: &mut &'_ str) -> PResult<Atom> {
+ alt((
+ digit1.try_map(|digit_str: &str| digit_str.parse::<i32>().map(Atom::Num)),
+ preceded("-", digit1).map(|digit_str: &str| Atom::Num(-digit_str.parse::<i32>().unwrap())),
+ ))
+ .parse_next(i)
+}
+
+/// Our boolean values are also constant, so we can do it the same way
+fn parse_bool(i: &mut &'_ str) -> PResult<Atom> {
+ alt((
+ "#t".map(|_| Atom::Boolean(true)),
+ "#f".map(|_| Atom::Boolean(false)),
+ ))
+ .parse_next(i)
+}
+
+fn parse_builtin(i: &mut &'_ str) -> PResult<BuiltIn> {
+ // alt gives us the result of first parser that succeeds, of the series of
+ // parsers we give it
+ alt((
+ parse_builtin_op,
+ // map lets us process the parsed output, in this case we know what we parsed,
+ // so we ignore the input and return the BuiltIn directly
+ "not".map(|_| BuiltIn::Not),
+ ))
+ .parse_next(i)
+}
+
+/// Continuing the trend of starting from the simplest piece and building up,
+/// we start by creating a parser for the built-in operator functions.
+fn parse_builtin_op(i: &mut &'_ str) -> PResult<BuiltIn> {
+ // one_of matches one of the characters we give it
+ let t = one_of(['+', '-', '*', '/', '=']).parse_next(i)?;
+
+ // because we are matching single character tokens, we can do the matching logic
+ // on the returned value
+ Ok(match t {
+ '+' => BuiltIn::Plus,
+ '-' => BuiltIn::Minus,
+ '*' => BuiltIn::Times,
+ '/' => BuiltIn::Divide,
+ '=' => BuiltIn::Equal,
+ _ => unreachable!(),
+ })
+}
+
+/// The next easiest thing to parse are keywords.
+/// We introduce some error handling combinators: `context` for human readable errors
+/// and `cut_err` to prevent back-tracking.
+///
+/// Put plainly: `preceded(":", cut_err(alpha1))` means that once we see the `:`
+/// character, we have to see one or more alphabetic characters or the input is invalid.
+fn parse_keyword(i: &mut &'_ str) -> PResult<Atom> {
+ preceded(":", cut_err(alpha1))
+ .context(StrContext::Label("keyword"))
+ .map(|sym_str: &str| Atom::Keyword(sym_str.to_string()))
+ .parse_next(i)
+}
+
+/// We can now use our new combinator to define the rest of the `Expr`s.
+///
+/// Starting with function application, we can see how the parser mirrors our data
+/// definitions: our definition is `Application(Box<Expr>, Vec<Expr>)`, so we know
+/// that we need to parse an expression and then parse 0 or more expressions, all
+/// wrapped in an S-expression.
+///
+/// tuples are themselves a parser, used to sequence parsers together, so we can translate this
+/// directly and then map over it to transform the output into an `Expr::Application`
+fn parse_application(i: &mut &'_ str) -> PResult<Expr> {
+ let application_inner = (parse_expr, repeat(0.., parse_expr))
+ .map(|(head, tail)| Expr::Application(Box::new(head), tail));
+ // finally, we wrap it in an s-expression
+ s_exp(application_inner).parse_next(i)
+}
+
+/// Because `Expr::If` and `Expr::IfElse` are so similar (we easily could have
+/// defined `Expr::If` to have an `Option` for the else block), we parse both
+/// in a single function.
+///
+/// In fact, we define our parser as if `Expr::If` was defined with an Option in it,
+/// we have the `opt` combinator which fits very nicely here.
+fn parse_if(i: &mut &'_ str) -> PResult<Expr> {
+ let if_inner = preceded(
+ // here to avoid ambiguity with other names starting with `if`, if we added
+ // variables to our language, we say that if must be terminated by at least
+ // one whitespace character
+ terminated("if", multispace1),
+ cut_err((parse_expr, parse_expr, opt(parse_expr))),
+ )
+ .map(|(pred, true_branch, maybe_false_branch)| {
+ if let Some(false_branch) = maybe_false_branch {
+ Expr::IfElse(
+ Box::new(pred),
+ Box::new(true_branch),
+ Box::new(false_branch),
+ )
+ } else {
+ Expr::If(Box::new(pred), Box::new(true_branch))
+ }
+ })
+ .context(StrContext::Label("if expression"));
+ s_exp(if_inner).parse_next(i)
+}
+
+/// A quoted S-expression is list data structure.
+///
+/// This example doesn't have the symbol atom, but by adding variables and changing
+/// the definition of quote to not always be around an S-expression, we'd get them
+/// naturally.
+fn parse_quote(i: &mut &'_ str) -> PResult<Expr> {
+ // this should look very straight-forward after all we've done:
+ // we find the `'` (quote) character, use cut_err to say that we're unambiguously
+ // looking for an s-expression of 0 or more expressions, and then parse them
+ preceded("'", cut_err(s_exp(repeat(0.., parse_expr))))
+ .context(StrContext::Label("quote"))
+ .map(Expr::Quote)
+ .parse_next(i)
+}
+
+/// Before continuing, we need a helper function to parse lists.
+/// A list starts with `(` and ends with a matching `)`.
+/// By putting whitespace and newline parsing here, we can avoid having to worry about it
+/// in much of the rest of the parser.
+//.parse_next/
+/// Unlike the previous functions, this function doesn't take or consume input, instead it
+/// takes a parsing function and returns a new parsing function.
+fn s_exp<'a, O1, F>(inner: F) -> impl Parser<&'a str, O1, ContextError>
+where
+ F: Parser<&'a str, O1, ContextError>,
+{
+ delimited(
+ '(',
+ preceded(multispace0, inner),
+ cut_err(preceded(multispace0, ')')).context(StrContext::Label("closing paren")),
+ )
+}
+
+/// And that's it!
+/// We can now parse our entire lisp language.
+///
+/// But in order to make it a little more interesting, we can hack together
+/// a little interpreter to take an Expr, which is really an
+/// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST),
+/// and give us something back
+
+/// This function tries to reduce the AST.
+/// This has to return an Expression rather than an Atom because quoted `s_expressions`
+/// can't be reduced
+fn eval_expression(e: Expr) -> Option<Expr> {
+ match e {
+ // Constants and quoted s-expressions are our base-case
+ Expr::Constant(_) | Expr::Quote(_) => Some(e),
+ // we then recursively `eval_expression` in the context of our special forms
+ // and built-in operators
+ Expr::If(pred, true_branch) => {
+ let reduce_pred = eval_expression(*pred)?;
+ if get_bool_from_expr(reduce_pred)? {
+ eval_expression(*true_branch)
+ } else {
+ None
+ }
+ }
+ Expr::IfElse(pred, true_branch, false_branch) => {
+ let reduce_pred = eval_expression(*pred)?;
+ if get_bool_from_expr(reduce_pred)? {
+ eval_expression(*true_branch)
+ } else {
+ eval_expression(*false_branch)
+ }
+ }
+ Expr::Application(head, tail) => {
+ let reduced_head = eval_expression(*head)?;
+ let reduced_tail = tail
+ .into_iter()
+ .map(eval_expression)
+ .collect::<Option<Vec<Expr>>>()?;
+ if let Expr::Constant(Atom::BuiltIn(bi)) = reduced_head {
+ Some(Expr::Constant(match bi {
+ BuiltIn::Plus => Atom::Num(
+ reduced_tail
+ .into_iter()
+ .map(get_num_from_expr)
+ .collect::<Option<Vec<i32>>>()?
+ .into_iter()
+ .sum(),
+ ),
+ BuiltIn::Times => Atom::Num(
+ reduced_tail
+ .into_iter()
+ .map(get_num_from_expr)
+ .collect::<Option<Vec<i32>>>()?
+ .into_iter()
+ .product(),
+ ),
+ BuiltIn::Equal => Atom::Boolean(
+ reduced_tail
+ .iter()
+ .zip(reduced_tail.iter().skip(1))
+ .all(|(a, b)| a == b),
+ ),
+ BuiltIn::Not => {
+ if reduced_tail.len() != 1 {
+ return None;
+ } else {
+ Atom::Boolean(!get_bool_from_expr(
+ reduced_tail.first().cloned().unwrap(),
+ )?)
+ }
+ }
+ BuiltIn::Minus => {
+ Atom::Num(if let Some(first_elem) = reduced_tail.first().cloned() {
+ let fe = get_num_from_expr(first_elem)?;
+ reduced_tail
+ .into_iter()
+ .map(get_num_from_expr)
+ .collect::<Option<Vec<i32>>>()?
+ .into_iter()
+ .skip(1)
+ .fold(fe, |a, b| a - b)
+ } else {
+ Default::default()
+ })
+ }
+ BuiltIn::Divide => {
+ Atom::Num(if let Some(first_elem) = reduced_tail.first().cloned() {
+ let fe = get_num_from_expr(first_elem)?;
+ reduced_tail
+ .into_iter()
+ .map(get_num_from_expr)
+ .collect::<Option<Vec<i32>>>()?
+ .into_iter()
+ .skip(1)
+ .fold(fe, |a, b| a / b)
+ } else {
+ Default::default()
+ })
+ }
+ }))
+ } else {
+ None
+ }
+ }
+ }
+}
+
+/// To start we define a couple of helper functions
+fn get_num_from_expr(e: Expr) -> Option<i32> {
+ if let Expr::Constant(Atom::Num(n)) = e {
+ Some(n)
+ } else {
+ None
+ }
+}
+
+fn get_bool_from_expr(e: Expr) -> Option<bool> {
+ if let Expr::Constant(Atom::Boolean(b)) = e {
+ Some(b)
+ } else {
+ None
+ }
+}
diff --git a/examples/string/main.rs b/examples/string/main.rs
new file mode 100644
index 0000000..0c2647e
--- /dev/null
+++ b/examples/string/main.rs
@@ -0,0 +1,70 @@
+//! This example shows an example of how to parse an escaped string. The
+//! rules for the string are similar to JSON and rust. A string is:
+//!
+//! - Enclosed by double quotes
+//! - Can contain any raw unescaped code point besides \ and "
+//! - Matches the following escape sequences: \b, \f, \n, \r, \t, \", \\, \/
+//! - Matches code points like Rust: \u{XXXX}, where XXXX can be up to 6
+//! hex characters
+//! - an escape followed by whitespace consumes all whitespace between the
+//! escape and the next non-whitespace character
+
+#![cfg(feature = "alloc")]
+
+mod parser;
+
+use winnow::prelude::*;
+
+fn main() -> Result<(), lexopt::Error> {
+ let args = Args::parse()?;
+
+ let data = args.input.as_deref().unwrap_or("\"abc\"");
+ let result = parser::parse_string::<()>.parse(data);
+ match result {
+ Ok(data) => println!("{}", data),
+ Err(err) => println!("{:?}", err),
+ }
+
+ Ok(())
+}
+
+#[derive(Default)]
+struct Args {
+ input: Option<String>,
+}
+
+impl Args {
+ fn parse() -> Result<Self, lexopt::Error> {
+ use lexopt::prelude::*;
+
+ let mut res = Args::default();
+
+ let mut args = lexopt::Parser::from_env();
+ while let Some(arg) = args.next()? {
+ match arg {
+ Value(input) => {
+ res.input = Some(input.string()?);
+ }
+ _ => return Err(arg.unexpected()),
+ }
+ }
+ Ok(res)
+ }
+}
+
+#[test]
+fn simple() {
+ let data = "\"abc\"";
+ let result = parser::parse_string::<()>.parse(data);
+ assert_eq!(result, Ok(String::from("abc")));
+}
+
+#[test]
+fn escaped() {
+ let data = "\"tab:\\tafter tab, newline:\\nnew line, quote: \\\", emoji: \\u{1F602}, newline:\\nescaped whitespace: \\ abc\"";
+ let result = parser::parse_string::<()>.parse(data);
+ assert_eq!(
+ result,
+ Ok(String::from("tab:\tafter tab, newline:\nnew line, quote: \", emoji: 😂, newline:\nescaped whitespace: abc"))
+ );
+}
diff --git a/examples/string/parser.rs b/examples/string/parser.rs
new file mode 100644
index 0000000..6b63458
--- /dev/null
+++ b/examples/string/parser.rs
@@ -0,0 +1,165 @@
+//! This example shows an example of how to parse an escaped string. The
+//! rules for the string are similar to JSON and rust. A string is:
+//!
+//! - Enclosed by double quotes
+//! - Can contain any raw unescaped code point besides \ and "
+//! - Matches the following escape sequences: \b, \f, \n, \r, \t, \", \\, \/
+//! - Matches code points like Rust: \u{XXXX}, where XXXX can be up to 6
+//! hex characters
+//! - an escape followed by whitespace consumes all whitespace between the
+//! escape and the next non-whitespace character
+
+use winnow::ascii::multispace1;
+use winnow::combinator::alt;
+use winnow::combinator::fold_repeat;
+use winnow::combinator::{delimited, preceded};
+use winnow::error::{FromExternalError, ParserError};
+use winnow::prelude::*;
+use winnow::token::{take_till1, take_while};
+
+/// Parse a string. Use a loop of `parse_fragment` and push all of the fragments
+/// into an output string.
+pub fn parse_string<'a, E>(input: &mut &'a str) -> PResult<String, E>
+where
+ E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
+{
+ // fold_repeat is the equivalent of iterator::fold. It runs a parser in a loop,
+ // and for each output value, calls a folding function on each output value.
+ let build_string = fold_repeat(
+ 0..,
+ // Our parser function – parses a single string fragment
+ parse_fragment,
+ // Our init value, an empty string
+ String::new,
+ // Our folding function. For each fragment, append the fragment to the
+ // string.
+ |mut string, fragment| {
+ match fragment {
+ StringFragment::Literal(s) => string.push_str(s),
+ StringFragment::EscapedChar(c) => string.push(c),
+ StringFragment::EscapedWS => {}
+ }
+ string
+ },
+ );
+
+ // Finally, parse the string. Note that, if `build_string` could accept a raw
+ // " character, the closing delimiter " would never match. When using
+ // `delimited` with a looping parser (like fold_repeat), be sure that the
+ // loop won't accidentally match your closing delimiter!
+ delimited('"', build_string, '"').parse_next(input)
+}
+
+/// A string fragment contains a fragment of a string being parsed: either
+/// a non-empty Literal (a series of non-escaped characters), a single
+/// parsed escaped character, or a block of escaped whitespace.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum StringFragment<'a> {
+ Literal(&'a str),
+ EscapedChar(char),
+ EscapedWS,
+}
+
+/// Combine `parse_literal`, `parse_escaped_whitespace`, and `parse_escaped_char`
+/// into a `StringFragment`.
+fn parse_fragment<'a, E>(input: &mut &'a str) -> PResult<StringFragment<'a>, E>
+where
+ E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
+{
+ alt((
+ // The `map` combinator runs a parser, then applies a function to the output
+ // of that parser.
+ parse_literal.map(StringFragment::Literal),
+ parse_escaped_char.map(StringFragment::EscapedChar),
+ parse_escaped_whitespace.value(StringFragment::EscapedWS),
+ ))
+ .parse_next(input)
+}
+
+/// Parse a non-empty block of text that doesn't include \ or "
+fn parse_literal<'a, E: ParserError<&'a str>>(input: &mut &'a str) -> PResult<&'a str, E> {
+ // `take_till1` parses a string of 0 or more characters that aren't one of the
+ // given characters.
+ let not_quote_slash = take_till1(['"', '\\']);
+
+ // `verify` runs a parser, then runs a verification function on the output of
+ // the parser. The verification function accepts the output only if it
+ // returns true. In this case, we want to ensure that the output of take_till1
+ // is non-empty.
+ not_quote_slash
+ .verify(|s: &str| !s.is_empty())
+ .parse_next(input)
+}
+
+// parser combinators are constructed from the bottom up:
+// first we write parsers for the smallest elements (escaped characters),
+// then combine them into larger parsers.
+
+/// Parse an escaped character: \n, \t, \r, \u{00AC}, etc.
+fn parse_escaped_char<'a, E>(input: &mut &'a str) -> PResult<char, E>
+where
+ E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
+{
+ preceded(
+ '\\',
+ // `alt` tries each parser in sequence, returning the result of
+ // the first successful match
+ alt((
+ parse_unicode,
+ // The `value` parser returns a fixed value (the first argument) if its
+ // parser (the second argument) succeeds. In these cases, it looks for
+ // the marker characters (n, r, t, etc) and returns the matching
+ // character (\n, \r, \t, etc).
+ 'n'.value('\n'),
+ 'r'.value('\r'),
+ 't'.value('\t'),
+ 'b'.value('\u{08}'),
+ 'f'.value('\u{0C}'),
+ '\\'.value('\\'),
+ '/'.value('/'),
+ '"'.value('"'),
+ )),
+ )
+ .parse_next(input)
+}
+
+/// Parse a unicode sequence, of the form u{XXXX}, where XXXX is 1 to 6
+/// hexadecimal numerals. We will combine this later with `parse_escaped_char`
+/// to parse sequences like \u{00AC}.
+fn parse_unicode<'a, E>(input: &mut &'a str) -> PResult<char, E>
+where
+ E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
+{
+ // `take_while` parses between `m` and `n` bytes (inclusive) that match
+ // a predicate. `parse_hex` here parses between 1 and 6 hexadecimal numerals.
+ let parse_hex = take_while(1..=6, |c: char| c.is_ascii_hexdigit());
+
+ // `preceded` takes a prefix parser, and if it succeeds, returns the result
+ // of the body parser. In this case, it parses u{XXXX}.
+ let parse_delimited_hex = preceded(
+ 'u',
+ // `delimited` is like `preceded`, but it parses both a prefix and a suffix.
+ // It returns the result of the middle parser. In this case, it parses
+ // {XXXX}, where XXXX is 1 to 6 hex numerals, and returns XXXX
+ delimited('{', parse_hex, '}'),
+ );
+
+ // `try_map` takes the result of a parser and applies a function that returns
+ // a Result. In this case we take the hex bytes from parse_hex and attempt to
+ // convert them to a u32.
+ let parse_u32 = parse_delimited_hex.try_map(move |hex| u32::from_str_radix(hex, 16));
+
+ // verify_map is like try_map, but it takes an Option instead of a Result. If
+ // the function returns None, verify_map returns an error. In this case, because
+ // not all u32 values are valid unicode code points, we have to fallibly
+ // convert to char with from_u32.
+ parse_u32.verify_map(std::char::from_u32).parse_next(input)
+}
+
+/// Parse a backslash, followed by any amount of whitespace. This is used later
+/// to discard any escaped whitespace.
+fn parse_escaped_whitespace<'a, E: ParserError<&'a str>>(
+ input: &mut &'a str,
+) -> PResult<&'a str, E> {
+ preceded('\\', multispace1).parse_next(input)
+}
diff --git a/src/_topic/arithmetic.rs b/src/_topic/arithmetic.rs
new file mode 100644
index 0000000..1a6eddc
--- /dev/null
+++ b/src/_topic/arithmetic.rs
@@ -0,0 +1,13 @@
+//! # Arithmetic
+//!
+//! ## Direct evaluation
+//!
+//! ```rust
+#![doc = include_str!("../../examples/arithmetic/parser.rs")]
+//! ```
+//!
+//! ## Parse to AST
+//!
+//! ```rust
+#![doc = include_str!("../../examples/arithmetic/parser_ast.rs")]
+//! ```
diff --git a/src/_topic/error.rs b/src/_topic/error.rs
new file mode 100644
index 0000000..c5374b4
--- /dev/null
+++ b/src/_topic/error.rs
@@ -0,0 +1,13 @@
+//! # Custom Errors
+//!
+//! The most basic error type is [`ParserError`][crate::error::ParserError]
+//!
+//! Optional traits include:
+//! - [`AddContext`][crate::error::AddContext]
+//! - [`FromExternalError`][crate::error::FromExternalError]
+//!
+//! # Example
+//!
+//!```rust
+#![doc = include_str!("../../examples/custom_error.rs")]
+//!```
diff --git a/src/_topic/fromstr.rs b/src/_topic/fromstr.rs
new file mode 100644
index 0000000..d126d16
--- /dev/null
+++ b/src/_topic/fromstr.rs
@@ -0,0 +1,8 @@
+//! # Implementing `FromStr`
+//!
+//! The [`FromStr` trait][std::str::FromStr] provides
+//! a common interface to parse from a string.
+//!
+//! ```rust
+#![doc = include_str!("../../examples/css/parser.rs")]
+//! ```
diff --git a/src/_topic/http.rs b/src/_topic/http.rs
new file mode 100644
index 0000000..5001694
--- /dev/null
+++ b/src/_topic/http.rs
@@ -0,0 +1,5 @@
+//! # HTTP
+//!
+//! ```rust
+#![doc = include_str!("../../examples/http/parser.rs")]
+//! ```
diff --git a/src/_topic/ini.rs b/src/_topic/ini.rs
new file mode 100644
index 0000000..b78ade5
--- /dev/null
+++ b/src/_topic/ini.rs
@@ -0,0 +1,5 @@
+//! # INI
+//!
+//! ```rust
+#![doc = include_str!("../../examples/ini/parser.rs")]
+//! ```
diff --git a/src/_topic/json.rs b/src/_topic/json.rs
new file mode 100644
index 0000000..fb4df3b
--- /dev/null
+++ b/src/_topic/json.rs
@@ -0,0 +1,5 @@
+//! # json
+//!
+//! ```rust,ignore
+#![doc = include_str!("../../examples/json/parser.rs")]
+//! ```
diff --git a/src/_topic/language.rs b/src/_topic/language.rs
new file mode 100644
index 0000000..0cebc99
--- /dev/null
+++ b/src/_topic/language.rs
@@ -0,0 +1,330 @@
+//! # Elements of Programming Languages
+//!
+//! These are short recipes for accomplishing common tasks.
+//!
+//! * [Whitespace](#whitespace)
+//! + [Wrapper combinators that eat whitespace before and after a parser](#wrapper-combinators-that-eat-whitespace-before-and-after-a-parser)
+//! * [Comments](#comments)
+//! + [`// C++/EOL-style comments`](#-ceol-style-comments)
+//! + [`/* C-style comments */`](#-c-style-comments-)
+//! * [Identifiers](#identifiers)
+//! + [`Rust-Style Identifiers`](#rust-style-identifiers)
+//! * [Literal Values](#literal-values)
+//! + [Escaped Strings](#escaped-strings)
+//! + [Integers](#integers)
+//! - [Hexadecimal](#hexadecimal)
+//! - [Octal](#octal)
+//! - [Binary](#binary)
+//! - [Decimal](#decimal)
+//! + [Floating Point Numbers](#floating-point-numbers)
+//!
+//! ## Whitespace
+//!
+//!
+//!
+//! ### Wrapper combinators that eat whitespace before and after a parser
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! error::ParserError,
+//! combinator::delimited,
+//! ascii::multispace0,
+//! };
+//!
+//! /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and
+//! /// trailing whitespace, returning the output of `inner`.
+//! fn ws<'a, F, O, E: ParserError<&'a str>>(inner: F) -> impl Parser<&'a str, O, E>
+//! where
+//! F: Parser<&'a str, O, E>,
+//! {
+//! delimited(
+//! multispace0,
+//! inner,
+//! multispace0
+//! )
+//! }
+//! ```
+//!
+//! To eat only trailing whitespace, replace `delimited(...)` with `terminated(&inner, multispace0)`.
+//! Likewise, the eat only leading whitespace, replace `delimited(...)` with `preceded(multispace0,
+//! &inner)`. You can use your own parser instead of `multispace0` if you want to skip a different set
+//! of lexemes.
+//!
+//! ## Comments
+//!
+//! ### `// C++/EOL-style comments`
+//!
+//! This version uses `%` to start a comment, does not consume the newline character, and returns an
+//! output of `()`.
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! error::ParserError,
+//! token::take_till1,
+//! };
+//!
+//! pub fn peol_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E>
+//! {
+//! ('%', take_till1(['\n', '\r']))
+//! .void() // Output is thrown away.
+//! .parse_next(i)
+//! }
+//! ```
+//!
+//! ### `/* C-style comments */`
+//!
+//! Inline comments surrounded with sentinel tags `(*` and `*)`. This version returns an output of `()`
+//! and does not handle nested comments.
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! error::ParserError,
+//! token::{tag, take_until0},
+//! };
+//!
+//! pub fn pinline_comment<'a, E: ParserError<&'a str>>(i: &mut &'a str) -> PResult<(), E> {
+//! (
+//! "(*",
+//! take_until0("*)"),
+//! "*)"
+//! )
+//! .void() // Output is thrown away.
+//! .parse_next(i)
+//! }
+//! ```
+//!
+//! ## Identifiers
+//!
+//! ### `Rust-Style Identifiers`
+//!
+//! Parsing identifiers that may start with a letter (or underscore) and may contain underscores,
+//! letters and numbers may be parsed like this:
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! stream::AsChar,
+//! token::take_while,
+//! token::one_of,
+//! };
+//!
+//! pub fn identifier<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! (
+//! one_of(|c: char| c.is_alpha() || c == '_'),
+//! take_while(0.., |c: char| c.is_alphanum() || c == '_')
+//! )
+//! .recognize()
+//! .parse_next(input)
+//! }
+//! ```
+//!
+//! Let's say we apply this to the identifier `hello_world123abc`. The first element of the tuple
+//! would uses [`one_of`][crate::token::one_of] which would recognize `h`. The tuple ensures that
+//! `ello_world123abc` will be piped to the next [`take_while`][crate::token::take_while] parser,
+//! which recognizes every remaining character. However, the tuple returns a tuple of the results
+//! of its sub-parsers. The [`recognize`][crate::Parser::recognize] parser produces a `&str` of the
+//! input text that was parsed, which in this case is the entire `&str` `hello_world123abc`.
+//!
+//! ## Literal Values
+//!
+//! ### Escaped Strings
+//!
+//! ```rust
+#![doc = include_str!("../../examples/string/parser.rs")]
+//! ```
+//!
+//! See also [`escaped`] and [`escaped_transform`].
+//!
+//! ### Integers
+//!
+//! The following recipes all return string slices rather than integer values. How to obtain an
+//! integer value instead is demonstrated for hexadecimal integers. The others are similar.
+//!
+//! The parsers allow the grouping character `_`, which allows one to group the digits by byte, for
+//! example: `0xA4_3F_11_28`. If you prefer to exclude the `_` character, the lambda to convert from a
+//! string slice to an integer value is slightly simpler. You can also strip the `_` from the string
+//! slice that is returned, which is demonstrated in the second hexadecimal number parser.
+//!
+//! #### Hexadecimal
+//!
+//! The parser outputs the string slice of the digits without the leading `0x`/`0X`.
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! combinator::alt,
+//! combinator::{repeat},
+//! combinator::{preceded, terminated},
+//! token::one_of,
+//! token::tag,
+//! };
+//!
+//! fn hexadecimal<'s>(input: &mut &'s str) -> PResult<&'s str> { // <'a, E: ParserError<&'a str>>
+//! preceded(
+//! alt(("0x", "0X")),
+//! repeat(1..,
+//! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ()))
+//! ).map(|()| ()).recognize()
+//! ).parse_next(input)
+//! }
+//! ```
+//!
+//! If you want it to return the integer value instead, use map:
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! combinator::alt,
+//! combinator::{repeat},
+//! combinator::{preceded, terminated},
+//! token::one_of,
+//! token::tag,
+//! };
+//!
+//! fn hexadecimal_value(input: &mut &str) -> PResult<i64> {
+//! preceded(
+//! alt(("0x", "0X")),
+//! repeat(1..,
+//! terminated(one_of(('0'..='9', 'a'..='f', 'A'..='F')), repeat(0.., '_').map(|()| ()))
+//! ).map(|()| ()).recognize()
+//! ).try_map(
+//! |out: &str| i64::from_str_radix(&str::replace(&out, "_", ""), 16)
+//! ).parse_next(input)
+//! }
+//! ```
+//!
+//! See also [`hex_uint`]
+//!
+//! #### Octal
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! combinator::alt,
+//! combinator::{repeat},
+//! combinator::{preceded, terminated},
+//! token::one_of,
+//! token::tag,
+//! };
+//!
+//! fn octal<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! preceded(
+//! alt(("0o", "0O")),
+//! repeat(1..,
+//! terminated(one_of('0'..='7'), repeat(0.., '_').map(|()| ()))
+//! ).map(|()| ()).recognize()
+//! ).parse_next(input)
+//! }
+//! ```
+//!
+//! #### Binary
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! combinator::alt,
+//! combinator::{repeat},
+//! combinator::{preceded, terminated},
+//! token::one_of,
+//! token::tag,
+//! };
+//!
+//! fn binary<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! preceded(
+//! alt(("0b", "0B")),
+//! repeat(1..,
+//! terminated(one_of('0'..='1'), repeat(0.., '_').map(|()| ()))
+//! ).map(|()| ()).recognize()
+//! ).parse_next(input)
+//! }
+//! ```
+//!
+//! #### Decimal
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! combinator::{repeat},
+//! combinator::terminated,
+//! token::one_of,
+//! };
+//!
+//! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! repeat(1..,
+//! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ()))
+//! ).map(|()| ())
+//! .recognize()
+//! .parse_next(input)
+//! }
+//! ```
+//!
+//! See also [`dec_uint`] and [`dec_int`]
+//!
+//! ### Floating Point Numbers
+//!
+//! The following is adapted from [the Python parser by Valentin Lorentz](https://github.com/ProgVal/rust-python-parser/blob/master/src/numbers.rs).
+//!
+//! ```rust
+//! use winnow::prelude::*;
+//! use winnow::{
+//! combinator::alt,
+//! combinator::{repeat},
+//! combinator::opt,
+//! combinator::{preceded, terminated},
+//! token::one_of,
+//! };
+//!
+//! fn float<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! alt((
+//! // Case one: .42
+//! (
+//! '.',
+//! decimal,
+//! opt((
+//! one_of(['e', 'E']),
+//! opt(one_of(['+', '-'])),
+//! decimal
+//! ))
+//! ).recognize()
+//! , // Case two: 42e42 and 42.42e42
+//! (
+//! decimal,
+//! opt(preceded(
+//! '.',
+//! decimal,
+//! )),
+//! one_of(['e', 'E']),
+//! opt(one_of(['+', '-'])),
+//! decimal
+//! ).recognize()
+//! , // Case three: 42. and 42.42
+//! (
+//! decimal,
+//! '.',
+//! opt(decimal)
+//! ).recognize()
+//! )).parse_next(input)
+//! }
+//!
+//! fn decimal<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! repeat(1..,
+//! terminated(one_of('0'..='9'), repeat(0.., '_').map(|()| ()))
+//! ).
+//! map(|()| ())
+//! .recognize()
+//! .parse_next(input)
+//! }
+//! ```
+//!
+//! See also [`float`]
+
+#![allow(unused_imports)]
+use crate::ascii::dec_int;
+use crate::ascii::dec_uint;
+use crate::ascii::escaped;
+use crate::ascii::escaped_transform;
+use crate::ascii::float;
+use crate::ascii::hex_uint;
diff --git a/src/_topic/mod.rs b/src/_topic/mod.rs
new file mode 100644
index 0000000..72c8145
--- /dev/null
+++ b/src/_topic/mod.rs
@@ -0,0 +1,36 @@
+//! # 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;
diff --git a/src/_topic/partial.rs b/src/_topic/partial.rs
new file mode 100644
index 0000000..19895d3
--- /dev/null
+++ b/src/_topic/partial.rs
@@ -0,0 +1,46 @@
+//! # Parsing Partial Input
+//!
+//! Typically, the input being parsed is all in-memory, or is complete. Some data sources are too
+//! large to fit into memory, only allowing parsing an incomplete or [`Partial`] subset of the
+//! data, requiring incrementally parsing.
+//!
+//! By wrapping a stream, like `&[u8]`, with [`Partial`], parsers will report when the data is
+//! [`Incomplete`] and more input is [`Needed`], allowing the caller to stream-in additional data
+//! to be parsed. The data is then parsed a chunk at a time.
+//!
+//! Chunks are typically defined by either:
+//! - A header reporting the number of bytes, like with [`length_value`]
+//! - [`Partial`] can explicitly be changed to being complete once the specified bytes are
+//! acquired via [`StreamIsPartial::complete`].
+//! - A delimiter, like with [ndjson](http://ndjson.org/)
+//! - You can parse up-to the delimiter or do a `take_until0(delim).and_then(parser)`
+//!
+//! If the chunks are not homogeneous, a state machine will be needed to track what the expected
+//! parser is for the next chunk.
+//!
+//! Caveats:
+//! - `winnow` takes the approach of re-parsing from scratch. Chunks should be relatively small to
+//! prevent the re-parsing overhead from dominating.
+//! - Parsers like [`repeat`] do not know when an `eof` is from insufficient data or the end of the
+//! stream, causing them to always report [`Incomplete`].
+//!
+//! # Example
+//!
+//! `main.rs`:
+//! ```rust,ignore
+#![doc = include_str!("../../examples/ndjson/main.rs")]
+//! ```
+//!
+//! `parser.rs`:
+//! ```rust,ignore
+#![doc = include_str!("../../examples/ndjson/parser.rs")]
+//! ```
+
+#![allow(unused_imports)] // Used for intra-doc links
+
+use crate::binary::length_value;
+use crate::combinator::repeat;
+use crate::error::ErrMode::Incomplete;
+use crate::error::Needed;
+use crate::stream::Partial;
+use crate::stream::StreamIsPartial;
diff --git a/src/_topic/performance.rs b/src/_topic/performance.rs
new file mode 100644
index 0000000..8a6555a
--- /dev/null
+++ b/src/_topic/performance.rs
@@ -0,0 +1,55 @@
+//! # Performance
+//!
+//! ## Runtime Performance
+//!
+//! See also the general Rust [Performance Book](https://nnethercote.github.io/perf-book/)
+//!
+//! Tips
+//! - Try `cargo add winnow -F simd`. For some it offers significant performance improvements
+//! - When enough cases of an [`alt`] have unique prefixes, prefer [`dispatch`]
+//! - When parsing text, try to parse as bytes (`u8`) rather than `char`s ([`BStr`] can make
+//! debugging easier)
+//! - Find simplified subsets of the grammar to parse, falling back to the full grammar when it
+//! doesn't work. For example, when parsing json strings, parse them without support for escapes,
+//! falling back to escape support if it fails.
+//! - Watch for large return types. A surprising place these can show up is when chaining parsers
+//! with a tuple.
+//!
+//! ## Build-time Performance
+//!
+//! Returning complex types as `impl Trait` can negatively impact build times. This can hit in
+//! surprising cases like:
+//! ```rust
+//! # use winnow::prelude::*;
+//! fn foo<I, O, E>() -> impl Parser<I, O, E>
+//! # where
+//! # I: winnow::stream::Stream<Token=O>,
+//! # I: winnow::stream::StreamIsPartial,
+//! # E: winnow::error::ParserError<I>,
+//! {
+//! // ...some chained combinators...
+//! # winnow::token::any
+//! }
+//! ```
+//!
+//! Instead, wrap the combinators in a closure to simplify the type:
+//! ```rust
+//! # use winnow::prelude::*;
+//! fn foo<I, O, E>() -> impl Parser<I, O, E>
+//! # where
+//! # I: winnow::stream::Stream<Token=O>,
+//! # I: winnow::stream::StreamIsPartial,
+//! # E: winnow::error::ParserError<I>,
+//! {
+//! move |input: &mut I| {
+//! // ...some chained combinators...
+//! # winnow::token::any
+//! .parse_next(input)
+//! }
+//! }
+//! ```
+
+#![allow(unused_imports)]
+use crate::combinator::alt;
+use crate::combinator::dispatch;
+use crate::stream::BStr;
diff --git a/src/_topic/s_expression.rs b/src/_topic/s_expression.rs
new file mode 100644
index 0000000..ef66600
--- /dev/null
+++ b/src/_topic/s_expression.rs
@@ -0,0 +1,5 @@
+//! # s-expression
+//!
+//! ```rust
+#![doc = include_str!("../../examples/s_expression/parser.rs")]
+//! ```
diff --git a/src/_topic/stream.rs b/src/_topic/stream.rs
new file mode 100644
index 0000000..4f94a94
--- /dev/null
+++ b/src/_topic/stream.rs
@@ -0,0 +1,54 @@
+//! # 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<Output>`.
+//!
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::tag;
+//! # type MyStream<'i> = &'i str;
+//! # type Output<'i> = &'i str;
+//! fn parser<'s>(i: &mut MyStream<'s>) -> PResult<Output<'s>> {
+//! "test".parse_next(i)
+//! }
+//! ```
+//!
+//! Here are the traits 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::*;
diff --git a/src/_topic/why.rs b/src/_topic/why.rs
new file mode 100644
index 0000000..e0328f1
--- /dev/null
+++ b/src/_topic/why.rs
@@ -0,0 +1,98 @@
+//! # Why `winnow`?
+//!
+//! To answer this question, it will be useful to contrast this with other approaches to parsing.
+//!
+//! **Note:** This will focus on principles and priorities. For a deeper and wider wider
+//! comparison with other Rust parser libraries, see
+//! [parse-rosetta-rs](https://github.com/rosetta-rs/parse-rosetta-rs).
+//!
+//! ## Hand-written parsers
+//!
+//! Typically, a hand-written parser gives you the flexibility to get
+//! - Fast parse performance
+//! - Fast compile-time
+//! - Small binary sizes
+//! - High quality error message
+//! - Fewer dependencies to audit
+//!
+//! However, this comes at the cost of doing it all yourself, including
+//! - Optimizing for each of the above characteristics you care about
+//! - Ensuring the safety of any `unsafe` code (buffer overflows being a common bug with parsers)
+//! - Being aware of, familiar with, and correctly implement the relevant algorithms.
+//! matklad, who has written two rust compile frontends, commented
+//! ["I’ve implemented a production-grade Pratt parser once, but I no longer immediately understand that code :-)"](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html)
+//!
+//! This approach works well if:
+//! - Your format is small and is unlikely to change
+//! - Your format is large but you have people who can focus solely on parsing, like with large
+//! programming languages
+//!
+//! ## `winnow`
+//!
+//! Unlike traditional programming language parsers that use
+//! [lex](https://en.wikipedia.org/wiki/Lex_(software)) or
+//! [yacc](https://en.wikipedia.org/wiki/Yacc), you can think of `winnow` as a general version of
+//! the helpers you would create along the way to writing a hand-written parser.
+//!
+//! `winnow` includes support for:
+//! - Zero-copy parsing
+//! - [Parse traces][crate::trace] for easier debugging
+//! - [Streaming parsing][crate::Partial] for network communication or large file
+//! - [Stateful][crate::Stateful] parsers
+//!
+//! For binary formats, `winnow` includes:
+//! - [A hexadecimal view][crate::Bytes] in [traces][crate::trace]
+//! - [TLV](https://en.wikipedia.org/wiki/Type-length-value)
+//! - Some common parsers to help get started, like numbers
+//!
+//! For text formats, `winnow` includes:
+//! - [Tracking of spans][crate::Located]
+//! - [A textual view when parsing as bytes][crate::BStr] in [traces][crate::trace]
+//! - Ability to evaluate directly, parse to an AST, or lex and parse the format
+//!
+//! This works well for:
+//! - Prototyping for what will be a hand-written parser
+//! - When you want to minimize the work to evolve your format
+//! - When you don't have contributors focused solely on parsing and your grammar is large enough
+//! to be unwieldy to hand write.
+//!
+//! ## `nom`
+//!
+//! `winnow` is a fork of the venerable [`nom`](https://crates.io/crates/nom). The difference
+//! between them is largely in priorities. `nom` prioritizes:
+//! - Lower churn for existing users while `winnow` is trying to find ways to make things better
+//! for the parsers yet to be written.
+//! - Having a small core, relying on external crates like
+//! [`nom-locate`](https://crates.io/crates/nom_locate) and
+//! [`nom-supreme`](https://crates.io/crates/nom-supreme), encouraging flexibility among users
+//! and to not block users on new features being merged while `winnow` aims to include all the
+//! fundamentals for parsing to ensure the experience is cohesive and high quality.
+//!
+//! ## `chumsky`
+//!
+//! [`chumsky`](https://crates.io/crates/chumsky) is an up and coming parser-combinator library
+//! that includes advanced features like error recovery.
+//!
+//! Probably the biggest diverging philosophy is `chumsky`s stance:
+//!
+//! > "If you need to implement either `Parser` or `Strategy` by hand, that's a problem that needs fixing".
+//!
+//! This is under "batteries included" but it also ties into the feeling that `chumksy` acts more like
+//! a framework. Instead of composing together helpers, you are expected to do everything through
+//! their system to the point that it is non-trivial to implement their `Parser` trait and are
+//! encouraged to use the
+//! [`custom`](https://docs.rs/chumsky/0.9.0/chumsky/primitive/fn.custom.html) helper. This
+//! requires re-framing everything to fit within their model and makes the code harder to understand
+//! and debug as you are working with abstract operations that will eventually be applied
+//! rather than directly with the parsers.
+//!
+//! In contrast, `winnow` is an introspectable toolbox that can easily be customized at any level.
+//! Probably the biggest thing that `winnow` loses out on is optimizations from ["parse modes" via
+//! GATs](https://github.com/zesterer/chumsky/pull/82) which allows downstream parsers to tell
+//! upstream parsers when information will be discarded, allowing bypassing expensive operations,
+//! like allocations. This requires a lot more complex interaction with parsers that isn't as
+//! trivial to do with bare functions which would lose out on any of that side-band information.
+//! Instead, we work around this with things like the [`Accumulate`] trait.
+
+#![allow(unused_imports)]
+use crate::stream::Accumulate;
diff --git a/src/_tutorial/chapter_0.rs b/src/_tutorial/chapter_0.rs
new file mode 100644
index 0000000..4be64c4
--- /dev/null
+++ b/src/_tutorial/chapter_0.rs
@@ -0,0 +1,39 @@
+//! # Chapter 0: Introduction
+//!
+//! This tutorial assumes that you are:
+//! - Already familiar with Rust
+//! - Using `winnow` for the first time
+//!
+//! The focus will be on parsing in-memory strings (`&str`). Once done, you might want to check the
+//! [Special Topics][_topic] for more specialized topics or examples.
+//!
+//! ## About
+//!
+//! `winnow` is a parser-combinator library. In other words, it gives you tools to define:
+//! - "parsers", or functions that 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_content;
diff --git a/src/_tutorial/chapter_1.rs b/src/_tutorial/chapter_1.rs
new file mode 100644
index 0000000..a2af005
--- /dev/null
+++ b/src/_tutorial/chapter_1.rs
@@ -0,0 +1,86 @@
+//! # Chapter 1: The Winnow Way
+//!
+//! First of all, we need to understand the way that winnow thinks about parsing.
+//! As discussed in the introduction, winnow lets us build simple parsers, and
+//! then combine them (using "combinators").
+//!
+//! Let's discuss what a "parser" actually does. A parser takes an input and returns
+//! a result, where:
+//! - `Ok` indicates the parser successfully found what it was looking for; or
+//! - `Err` indicates the parser could not find what it was looking for.
+//!
+//! Parsers do more than just return a binary "success"/"failure" code.
+//! On success, the parser will return the processed data. The input will be left pointing to
+//! data that still needs processing
+//!
+//! If the parser failed, then there are multiple errors that could be returned.
+//! For simplicity, however, in the next chapters we will leave these unexplored.
+//!
+//! ```text
+//! ┌─► Ok(what matched the parser)
+//! ┌─────────┐ │
+//! my input───►│my parser├──►either──┤
+//! └─────────┘ └─► Err(...)
+//! ```
+//!
+//!
+//! To represent this model of the world, winnow uses the [`PResult<O>`] type.
+//! The `Ok` variant has `output: O`;
+//! whereas the `Err` variant stores an error.
+//!
+//! You can import that from:
+//!
+//! ```rust
+//! use winnow::PResult;
+//! ```
+//!
+//! To combine parsers, we need a common way to refer to them which is where the [`Parser<I, O, E>`]
+//! trait comes in with [`Parser::parse_next`] being the primary way to drive
+//! parsing forward.
+//!
+//! You'll note that `I` and `O` are parameterized -- while most of the examples in this book
+//! will be with `&str` (i.e. parsing a string); they do not have to be strings; nor do they
+//! have to be the same type (consider the simple example where `I = &str`, and `O = u64` -- this
+//! parses a string into an unsigned integer.)
+//!
+//!
+//! # Let's write our first parser!
+//!
+//! The simplest parser we can write is one which successfully does nothing.
+//!
+//! To make it easier to implement a [`Parser`], the trait is implemented for
+//! functions of the form `Fn(&mut I) -> PResult<O>`.
+//!
+//! This parser function should take in a `&str`:
+//!
+//! - Since it is supposed to succeed, we know it will return the Ok Variant.
+//! - Since it does nothing to our input, the remaining input is the same as the input.
+//! - Since it doesn't parse anything, it also should just return an empty string.
+//!
+//! ```rust
+//! use winnow::PResult;
+//! use winnow::Parser;
+//!
+//! pub fn do_nothing_parser<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! Ok("")
+//! }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let output = do_nothing_parser.parse_next(&mut input).unwrap();
+//! // Same as:
+//! // let output = do_nothing_parser(&mut input).unwrap();
+//!
+//! assert_eq!(input, "0x1a2b Hello");
+//! assert_eq!(output, "");
+//! }
+//! ```
+
+#![allow(unused_imports)]
+use crate::PResult;
+use crate::Parser;
+
+pub use super::chapter_0 as previous;
+pub use super::chapter_2 as next;
+pub use crate::_tutorial as table_of_content;
diff --git a/src/_tutorial/chapter_2.rs b/src/_tutorial/chapter_2.rs
new file mode 100644
index 0000000..d3593b9
--- /dev/null
+++ b/src/_tutorial/chapter_2.rs
@@ -0,0 +1,248 @@
+//! # Chapter 2: Tokens and Tags
+//!
+//! The simplest *useful* parser you can write is one which matches tokens.
+//!
+//! ## Tokens
+//!
+//! [`Stream`] provides some core operations to help with parsing. For example, to process a
+//! single token, you can do:
+//! ```rust
+//! # use winnow::Parser;
+//! # use winnow::PResult;
+//! use winnow::stream::Stream;
+//! use winnow::error::ParserError;
+//! use winnow::error::ErrorKind;
+//! use winnow::error::ErrMode;
+//!
+//! fn parse_prefix(input: &mut &str) -> PResult<char> {
+//! let c = input.next_token().ok_or_else(|| {
+//! ErrMode::from_error_kind(input, ErrorKind::Token)
+//! })?;
+//! if c != '0' {
+//! return Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
+//! }
+//! Ok(c)
+//! }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let output = parse_prefix.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, "x1a2b Hello");
+//! assert_eq!(output, '0');
+//!
+//! assert!(parse_prefix.parse_next(&mut "d").is_err());
+//! }
+//! ```
+//!
+//! [`any`] and [`Parser::verify`] are [`Parser`] building blocks on top of [`Stream`]:
+//! ```rust
+//! # use winnow::PResult;
+//! use winnow::Parser;
+//! use winnow::token::any;
+//!
+//! fn parse_prefix(input: &mut &str) -> PResult<char> {
+//! any.verify(|c| *c == '0').parse_next(input)
+//! }
+//! #
+//! # fn main() {
+//! # let mut input = "0x1a2b Hello";
+//! #
+//! # let output = parse_prefix.parse_next(&mut input).unwrap();
+//! #
+//! # assert_eq!(input, "x1a2b Hello");
+//! # assert_eq!(output, '0');
+//! #
+//! # assert!(parse_prefix.parse_next(&mut "d").is_err());
+//! # }
+//! ```
+//!
+//! Matching a single token literal is common enough that [`Parser`] is implemented for
+//! `char`.
+//!
+//! ```rust
+//! # use winnow::PResult;
+//! use winnow::Parser;
+//!
+//! fn parse_prefix(input: &mut &str) -> PResult<char> {
+//! '0'.parse_next(input)
+//! }
+//! #
+//! # fn main() {
+//! # let mut input = "0x1a2b Hello";
+//! #
+//! # let output = parse_prefix.parse_next(&mut input).unwrap();
+//! #
+//! # assert_eq!(input, "x1a2b Hello");
+//! # assert_eq!(output, '0');
+//! #
+//! # assert!(parse_prefix.parse_next(&mut "d").is_err());
+//! # }
+//! ```
+//!
+//! ## Tags
+//!
+//! [`Stream`] also supports processing slices of tokens:
+//! ```rust
+//! # use winnow::Parser;
+//! # use winnow::PResult;
+//! use winnow::stream::Stream;
+//! use winnow::error::ParserError;
+//! use winnow::error::ErrorKind;
+//! use winnow::error::ErrMode;
+//!
+//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! let expected = "0x";
+//! if input.len() < expected.len() {
+//! return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+//! }
+//! let actual = input.next_slice(expected.len());
+//! if actual != expected {
+//! return Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
+//! }
+//! Ok(actual)
+//! }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let output = parse_prefix.parse_next(&mut input).unwrap();
+//! assert_eq!(input, "1a2b Hello");
+//! assert_eq!(output, "0x");
+//!
+//! assert!(parse_prefix.parse_next(&mut "0o123").is_err());
+//! }
+//! ```
+//!
+//! Again, matching a literal is common enough that [`Parser`] is implemented for `&str`:
+//! ```rust
+//! # use winnow::PResult;
+//! use winnow::Parser;
+//!
+//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! "0x".parse_next(input)
+//! }
+//! #
+//! # fn main() {
+//! # let mut input = "0x1a2b Hello";
+//! #
+//! # let output = parse_prefix.parse_next(&mut input).unwrap();
+//! # assert_eq!(input, "1a2b Hello");
+//! # assert_eq!(output, "0x");
+//! #
+//! # assert!(parse_prefix.parse_next(&mut "0o123").is_err());
+//! # }
+//! ```
+//!
+//! In `winnow`, we call this type of parser a [`tag`]. See [`token`] for additional individual
+//! and token-slice parsers.
+//!
+//! ## Character Classes
+//!
+//! Selecting a single `char` or a [`tag`] is fairly limited. Sometimes, you will want to select one of several
+//! `chars` of a specific class, like digits. For this, we use the [`one_of`] parer:
+//!
+//! ```rust
+//! # use winnow::Parser;
+//! # use winnow::PResult;
+//! use winnow::token::one_of;
+//!
+//! fn parse_digits(input: &mut &str) -> PResult<char> {
+//! one_of(('0'..='9', 'a'..='f', 'A'..='F')).parse_next(input)
+//! }
+//!
+//! fn main() {
+//! let mut input = "1a2b Hello";
+//!
+//! let output = parse_digits.parse_next(&mut input).unwrap();
+//! assert_eq!(input, "a2b Hello");
+//! assert_eq!(output, '1');
+//!
+//! assert!(parse_digits.parse_next(&mut "Z").is_err());
+//! }
+//! ```
+//!
+//! > **Aside:** [`one_of`] might look straightforward, a function returning a value that implements `Parser`.
+//! > Let's look at it more closely as its used above (resolving all generic parameters):
+//! > ```rust
+//! > # use winnow::prelude::*;
+//! > # use winnow::error::InputError;
+//! > pub fn one_of<'i>(
+//! > list: &'static [char]
+//! > ) -> impl Parser<&'i str, char, InputError<&'i str>> {
+//! > // ...
+//! > # winnow::token::one_of(list)
+//! > }
+//! > ```
+//! > If you have not programmed in a language where functions are values, the type signature of the
+//! > [`one_of`] function might be a surprise.
+//! > The function [`one_of`] *returns a function*. The function it returns is a
+//! > `Parser`, taking a `&str` and returning an `PResult`. This is a common pattern in winnow for
+//! > configurable or stateful parsers.
+//!
+//! Some of character classes are common enough that a named parser is provided, like with:
+//! - [`line_ending`][crate::ascii::line_ending]: Recognizes an end of line (both `\n` and `\r\n`)
+//! - [`newline`][crate::ascii::newline]: Matches a newline character `\n`
+//! - [`tab`][crate::ascii::tab]: Matches a tab character `\t`
+//!
+//! You can then capture sequences of these characters with parsers like [`take_while`].
+//! ```rust
+//! # use winnow::Parser;
+//! # use winnow::PResult;
+//! use winnow::token::take_while;
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! take_while(1.., ('0'..='9', 'a'..='f', 'A'..='F')).parse_next(input)
+//! }
+//!
+//! fn main() {
+//! let mut input = "1a2b Hello";
+//!
+//! let output = parse_digits.parse_next(&mut input).unwrap();
+//! assert_eq!(input, " Hello");
+//! assert_eq!(output, "1a2b");
+//!
+//! assert!(parse_digits.parse_next(&mut "Z").is_err());
+//! }
+//! ```
+//!
+//! We could simplify this further 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_content;
diff --git a/src/_tutorial/chapter_3.rs b/src/_tutorial/chapter_3.rs
new file mode 100644
index 0000000..4cbe487
--- /dev/null
+++ b/src/_tutorial/chapter_3.rs
@@ -0,0 +1,376 @@
+//! # Chapter 3: Sequencing and Alternatives
+//!
+//! In the last chapter, we saw how to create simple parsers using prebuilt parsers.
+//!
+//! In this chapter, we explore two other widely used features:
+//! alternatives and composition.
+//!
+//! ## Sequencing
+//!
+//! Now that we can create more interesting parsers, we can sequence them together, like:
+//!
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! #
+//! fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! "0x".parse_next(input)
+//! }
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! take_while(1.., (
+//! ('0'..='9'),
+//! ('A'..='F'),
+//! ('a'..='f'),
+//! )).parse_next(input)
+//! }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let prefix = parse_prefix.parse_next(&mut input).unwrap();
+//! let digits = parse_digits.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(prefix, "0x");
+//! assert_eq!(digits, "1a2b");
+//! assert_eq!(input, " Hello");
+//! }
+//! ```
+//!
+//! To sequence these together, you can just put them in a tuple:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! #
+//! # fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # "0x".parse_next(input)
+//! # }
+//! #
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! //...
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let (prefix, digits) = (
+//! parse_prefix,
+//! parse_digits
+//! ).parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(prefix, "0x");
+//! assert_eq!(digits, "1a2b");
+//! assert_eq!(input, " Hello");
+//! }
+//! ```
+//!
+//! Frequently, you won't care about the tag and you can instead use one of the provided combinators,
+//! like [`preceded`]:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! use winnow::combinator::preceded;
+//!
+//! # fn parse_prefix<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # "0x".parse_next(input)
+//! # }
+//! #
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! //...
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let digits = preceded(
+//! parse_prefix,
+//! parse_digits
+//! ).parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(digits, "1a2b");
+//! assert_eq!(input, " Hello");
+//! }
+//! ```
+//!
+//! See [`combinator`] for more sequencing parsers.
+//!
+//! ## Alternatives
+//!
+//! Sometimes, we might want to choose between two parsers; and we're happy with
+//! either being used.
+//!
+//! [`Stream::checkpoint`] helps us to retry parsing:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! use winnow::stream::Stream;
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! let start = input.checkpoint();
+//!
+//! if let Ok(output) = ("0b", parse_bin_digits).parse_next(input) {
+//! return Ok(output);
+//! }
+//!
+//! input.reset(start);
+//! if let Ok(output) = ("0o", parse_oct_digits).parse_next(input) {
+//! return Ok(output);
+//! }
+//!
+//! input.reset(start);
+//! if let Ok(output) = ("0d", parse_dec_digits).parse_next(input) {
+//! return Ok(output);
+//! }
+//!
+//! input.reset(start);
+//! ("0x", parse_hex_digits).parse_next(input)
+//! }
+//!
+//! // ...
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(prefix, "0x");
+//! assert_eq!(digits, "1a2b");
+//!
+//! assert!(parse_digits(&mut "ghiWorld").is_err());
+//! }
+//! ```
+//!
+//! > **Warning:** the above example is for illustrative purposes and relying on `Result::Ok` or
+//! > `Result::Err` can lead to incorrect behavior. This will be clarified in later when covering
+//! > [error handling][`chapter_6`#errmode]
+//!
+//! [`opt`] is a basic building block for correctly handling retrying parsing:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! use winnow::combinator::opt;
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! if let Some(output) = opt(("0b", parse_bin_digits)).parse_next(input)? {
+//! Ok(output)
+//! } else if let Some(output) = opt(("0o", parse_oct_digits)).parse_next(input)? {
+//! Ok(output)
+//! } else if let Some(output) = opt(("0d", parse_dec_digits)).parse_next(input)? {
+//! Ok(output)
+//! } else {
+//! ("0x", parse_hex_digits).parse_next(input)
+//! }
+//! }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn main() {
+//! # let mut input = "0x1a2b Hello";
+//! #
+//! # let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
+//! #
+//! # assert_eq!(input, " Hello");
+//! # assert_eq!(prefix, "0x");
+//! # assert_eq!(digits, "1a2b");
+//! #
+//! # assert!(parse_digits(&mut "ghiWorld").is_err());
+//! # }
+//! ```
+//!
+//! [`alt`] encapsulates this if/else-if ladder pattern, with the last case being the `else`:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! use winnow::combinator::alt;
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! alt((
+//! ("0b", parse_bin_digits),
+//! ("0o", parse_oct_digits),
+//! ("0d", parse_dec_digits),
+//! ("0x", parse_hex_digits),
+//! )).parse_next(input)
+//! }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn main() {
+//! # let mut input = "0x1a2b Hello";
+//! #
+//! # let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
+//! #
+//! # assert_eq!(input, " Hello");
+//! # assert_eq!(prefix, "0x");
+//! # assert_eq!(digits, "1a2b");
+//! #
+//! # assert!(parse_digits(&mut "ghiWorld").is_err());
+//! # }
+//! ```
+//!
+//! > **Note:** [`success`] and [`fail`] are parsers that might be useful in the `else` case.
+//!
+//! Sometimes a giant if/else-if ladder can be slow and you'd rather have a `match` statement for
+//! branches of your parser that have unique prefixes. In this case, you can use the
+//! [`dispatch`][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_content;
diff --git a/src/_tutorial/chapter_4.rs b/src/_tutorial/chapter_4.rs
new file mode 100644
index 0000000..e6a836b
--- /dev/null
+++ b/src/_tutorial/chapter_4.rs
@@ -0,0 +1,110 @@
+//! # Chapter 4: Parsers With Custom Return Types
+//!
+//! So far, we have seen mostly functions that take an `&str`, and return a
+//! `PResult<&str>`. Splitting strings into smaller strings and characters is certainly
+//! useful, but it's not the only thing winnow is capable of!
+//!
+//! A useful operation when parsing is to convert between types; for example
+//! parsing from `&str` to another primitive, like [`usize`].
+//!
+//! All we need to do for our parser to return a different type is to change
+//! the type parameter of [`PResult`] to the desired return type.
+//! For example, to return a `usize`, return a `PResult<usize>`.
+//! Recall that the type parameter of the `PResult` is the input
+//! type, so even if you're returning something different, if your input
+//! is a `&str`, the type argument of `PResult` should be also.
+//!
+//! One winnow-native way of doing a type conversion is to use the
+//! [`Parser::parse_to`] combinator
+//! to convert from a successful parse to a particular type using [`FromStr`].
+//!
+//! The following code converts from a string containing a number to `usize`:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::ascii::digit1;
+//! #
+//! fn parse_digits(input: &mut &str) -> PResult<usize> {
+//! digit1
+//! .parse_to()
+//! .parse_next(input)
+//! }
+//!
+//! fn main() {
+//! let mut input = "1024 Hello";
+//!
+//! let output = parse_digits.parse_next(&mut input).unwrap();
+//! assert_eq!(input, " Hello");
+//! assert_eq!(output, 1024);
+//!
+//! assert!(parse_digits(&mut "Z").is_err());
+//! }
+//! ```
+//!
+//! `Parser::parse_to` is just a convenient form of [`Parser::try_map`] which we can use to handle
+//! all radices of numbers:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! use winnow::combinator::dispatch;
+//! use winnow::token::take;
+//! use winnow::combinator::fail;
+//!
+//! fn parse_digits(input: &mut &str) -> PResult<usize> {
+//! dispatch!(take(2usize);
+//! "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! _ => fail,
+//! ).parse_next(input)
+//! }
+//!
+//! // ...
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let digits = parse_digits.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(digits, 0x1a2b);
+//!
+//! assert!(parse_digits(&mut "ghiWorld").is_err());
+//! }
+//! ```
+//!
+//! See also [`Parser`] for more output-modifying parsers.
+
+#![allow(unused_imports)]
+use crate::PResult;
+use crate::Parser;
+use std::str::FromStr;
+
+pub use super::chapter_3 as previous;
+pub use super::chapter_5 as next;
+pub use crate::_tutorial as table_of_content;
diff --git a/src/_tutorial/chapter_5.rs b/src/_tutorial/chapter_5.rs
new file mode 100644
index 0000000..2d5bac3
--- /dev/null
+++ b/src/_tutorial/chapter_5.rs
@@ -0,0 +1,282 @@
+//! # Chapter 5: Repetition
+//!
+//! In [`chapter_3`], we covered how to sequence different parsers into a tuple but sometimes you need to run a
+//! single parser multiple times, collecting the result into a container, like [`Vec`].
+//!
+//! Let's collect the result of `parse_digits`:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::dispatch;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! use winnow::combinator::opt;
+//! use winnow::combinator::repeat;
+//! use winnow::combinator::terminated;
+//!
+//! fn parse_list(input: &mut &str) -> PResult<Vec<usize>> {
+//! let mut list = Vec::new();
+//! while let Some(output) = opt(terminated(parse_digits, opt(','))).parse_next(input)? {
+//! list.push(output);
+//! }
+//! Ok(list)
+//! }
+//!
+//! // ...
+//! # fn parse_digits(input: &mut &str) -> PResult<usize> {
+//! # dispatch!(take(2usize);
+//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! # _ => fail,
+//! # ).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
+//!
+//! let digits = parse_list.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
+//!
+//! assert!(parse_digits(&mut "ghiWorld").is_err());
+//! }
+//! ```
+//!
+//! We can implement this declaratively with [`repeat`]:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::dispatch;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! use winnow::combinator::opt;
+//! use winnow::combinator::repeat;
+//! use winnow::combinator::terminated;
+//!
+//! fn parse_list(input: &mut &str) -> PResult<Vec<usize>> {
+//! repeat(0..,
+//! terminated(parse_digits, opt(','))
+//! ).parse_next(input)
+//! }
+//! #
+//! # fn parse_digits(input: &mut &str) -> PResult<usize> {
+//! # dispatch!(take(2usize);
+//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! # _ => fail,
+//! # ).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn main() {
+//! # let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
+//! #
+//! # let digits = parse_list.parse_next(&mut input).unwrap();
+//! #
+//! # assert_eq!(input, " Hello");
+//! # assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
+//! #
+//! # assert!(parse_digits(&mut "ghiWorld").is_err());
+//! # }
+//! ```
+//!
+//! You'll notice that the above allows trailing `,` when we intended to not support that. We can
+//! easily fix this by using [`separated0`]:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::dispatch;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! use winnow::combinator::separated0;
+//!
+//! fn parse_list(input: &mut &str) -> PResult<Vec<usize>> {
+//! separated0(parse_digits, ",").parse_next(input)
+//! }
+//!
+//! // ...
+//! # fn parse_digits(input: &mut &str) -> PResult<usize> {
+//! # dispatch!(take(2usize);
+//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! # _ => fail,
+//! # ).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
+//!
+//! let digits = parse_list.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
+//!
+//! assert!(parse_digits(&mut "ghiWorld").is_err());
+//! }
+//! ```
+//!
+//! If you look closely at [`repeat`], it isn't collecting directly into a [`Vec`] but
+//! [`Accumulate`] to gather the results. This 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<usize> {
+//! # dispatch!(take(2usize);
+//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! # _ => fail,
+//! # ).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
+//!
+//! let digits = recognize_list.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f");
+//!
+//! assert!(parse_digits(&mut "ghiWorld").is_err());
+//! }
+//! ```
+//! See [`combinator`] for more repetition parsers.
+
+#![allow(unused_imports)]
+use super::chapter_2;
+use super::chapter_3;
+use crate::combinator;
+use crate::combinator::repeat;
+use crate::combinator::separated0;
+use crate::stream::Accumulate;
+use crate::Parser;
+use std::vec::Vec;
+
+pub use super::chapter_4 as previous;
+pub use super::chapter_6 as next;
+pub use crate::_tutorial as table_of_content;
diff --git a/src/_tutorial/chapter_6.rs b/src/_tutorial/chapter_6.rs
new file mode 100644
index 0000000..ab21038
--- /dev/null
+++ b/src/_tutorial/chapter_6.rs
@@ -0,0 +1,156 @@
+//! # Chapter 6: Error Reporting
+//!
+//! ## `Error`
+//!
+//! Back in [`chapter_1`], we glossed over the `Err` side of [`PResult`]. `PResult<O>` is
+//! actually short for `PResult<O, E=ContextError>` where [`ContextError`] is a relatively cheap
+//! way of building up reasonable errors for humans.
+//!
+//! You can use [`Parser::context`] to annotate the error with custom types
+//! while unwinding to further improve the error quality.
+//!
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! use winnow::error::StrContext;
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! alt((
+//! ("0b", parse_bin_digits).context(StrContext::Label("binary")),
+//! ("0o", parse_oct_digits).context(StrContext::Label("octal")),
+//! ("0d", parse_dec_digits).context(StrContext::Label("decimal")),
+//! ("0x", parse_hex_digits).context(StrContext::Label("hexadecimal")),
+//! )).parse_next(input)
+//! }
+//!
+//! // ...
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(prefix, "0x");
+//! assert_eq!(digits, "1a2b");
+//! }
+//! ```
+//!
+//! At first glance, this looks correct but what `context` will be reported when parsing `"0b5"`?
+//! If you remember back to [`chapter_3`], [`alt`] will only report the last error by default which
+//! means when parsing `"0b5"`, the `context` will be `"hexadecimal"`.
+//!
+//! ## `ErrMode`
+//!
+//! Let's break down `PResult<O, E>` one step further:
+//! ```rust
+//! # use winnow::error::ErrorKind;
+//! # use winnow::error::ErrMode;
+//! pub type OResult<O, E = ErrorKind> = Result<O, ErrMode<E>>;
+//! ```
+//! [`PResult`] is just a fancy wrapper around `Result` that wraps our error in an [`ErrMode`]
+//! type.
+//!
+//! [`ErrMode`] is an enum with [`Backtrack`] and [`Cut`] variants (ignore [`Incomplete`] as its only
+//! relevant for [streaming][_topic::stream]). By default, errors are [`Backtrack`], meaning that
+//! other parsing branches will be attempted on failure, like the next case of an [`alt`]. [`Cut`]
+//! shortcircuits all other branches, immediately reporting the error.
+//!
+//! So we can get the correct `context` by modifying the above example with [`cut_err`]:
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::alt;
+//! # use winnow::error::StrContext;
+//! use winnow::combinator::cut_err;
+//!
+//! fn parse_digits<'s>(input: &mut &'s str) -> PResult<(&'s str, &'s str)> {
+//! alt((
+//! ("0b", cut_err(parse_bin_digits)).context(StrContext::Label("binary")),
+//! ("0o", cut_err(parse_oct_digits)).context(StrContext::Label("octal")),
+//! ("0d", cut_err(parse_dec_digits)).context(StrContext::Label("decimal")),
+//! ("0x", cut_err(parse_hex_digits)).context(StrContext::Label("hexadecimal")),
+//! )).parse_next(input)
+//! }
+//!
+//! // ...
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let mut input = "0x1a2b Hello";
+//!
+//! let (prefix, digits) = parse_digits.parse_next(&mut input).unwrap();
+//!
+//! assert_eq!(input, " Hello");
+//! assert_eq!(prefix, "0x");
+//! assert_eq!(digits, "1a2b");
+//! }
+//! ```
+//! Now, when parsing `"0b5"`, the `context` will be `"binary"`.
+
+#![allow(unused_imports)]
+use super::chapter_1;
+use super::chapter_3;
+use crate::combinator::alt;
+use crate::combinator::cut_err;
+use crate::error::ContextError;
+use crate::error::ErrMode;
+use crate::error::ErrMode::*;
+use crate::error::ErrorKind;
+use crate::PResult;
+use crate::Parser;
+use crate::_topic;
+
+pub use super::chapter_5 as previous;
+pub use super::chapter_7 as next;
+pub use crate::_tutorial as table_of_content;
diff --git a/src/_tutorial/chapter_7.rs b/src/_tutorial/chapter_7.rs
new file mode 100644
index 0000000..0a20d67
--- /dev/null
+++ b/src/_tutorial/chapter_7.rs
@@ -0,0 +1,118 @@
+//! # Chapter 7: Integrating the Parser
+//!
+//! So far, we've highlighted how to incrementally parse, but how do we bring this all together
+//! into our application?
+//!
+//! Parsers we've been working with look like:
+//! ```rust
+//! # use winnow::error::ContextError;
+//! # use winnow::error::ErrMode;
+//! # use winnow::Parser;
+//! #
+//! pub fn parser<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! // ...
+//! # Ok("")
+//! }
+//!
+//! type PResult<O> = Result<
+//! O,
+//! ErrMode<ContextError>
+//! >;
+//! ```
+//! 1. We have to decide what to do about the "remainder" of the `input`.
+//! 2. The [`ErrMode<ContextError>`] is not compatible with the rest of the Rust ecosystem.
+//! Normally, Rust applications want errors that are `std::error::Error + Send + Sync + 'static`
+//! meaning:
+//! - They implement the [`std::error::Error`] trait
+//! - They can be sent across threads
+//! - They are safe to be referenced across threads
+//! - They do not borrow
+//!
+//! winnow provides [`Parser::parse`] to help with this:
+//! - Ensures we hit [`eof`]
+//! - Removes the [`ErrMode`] wrapper
+//! - Wraps the error in [`ParseError`]
+//! - Provides access to the original [`input`][ParseError::input] with the
+//! [`offset`][ParseError::offset] of where it failed
+//! - Provides a default renderer (via [`std::fmt::Display`])
+//! ```rust
+//! # use winnow::prelude::*;
+//! # use winnow::token::take_while;
+//! # use winnow::combinator::dispatch;
+//! # use winnow::token::take;
+//! # use winnow::combinator::fail;
+//! use winnow::Parser;
+//!
+//! #[derive(Debug, PartialEq, Eq)]
+//! pub struct Hex(usize);
+//!
+//! impl std::str::FromStr for Hex {
+//! type Err = String;
+//!
+//! fn from_str(input: &str) -> Result<Self, Self::Err> {
+//! parse_digits
+//! .map(Hex)
+//! .parse(input)
+//! .map_err(|e| e.to_string())
+//! }
+//! }
+//!
+//! // ...
+//! # fn parse_digits<'s>(input: &mut &'s str) -> PResult<usize> {
+//! # dispatch!(take(2usize);
+//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
+//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
+//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
+//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
+//! # _ => fail,
+//! # ).parse_next(input)
+//! # }
+//! #
+//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='7'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # )).parse_next(input)
+//! # }
+//! #
+//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
+//! # take_while(1.., (
+//! # ('0'..='9'),
+//! # ('A'..='F'),
+//! # ('a'..='f'),
+//! # )).parse_next(input)
+//! # }
+//!
+//! fn main() {
+//! let input = "0x1a2b";
+//! assert_eq!(input.parse::<Hex>().unwrap(), Hex(0x1a2b));
+//!
+//! let input = "0x1a2b Hello";
+//! assert!(input.parse::<Hex>().is_err());
+//! let input = "ghiHello";
+//! assert!(input.parse::<Hex>().is_err());
+//! }
+//! ```
+
+#![allow(unused_imports)]
+use super::chapter_1;
+use crate::combinator::eof;
+use crate::error::ErrMode;
+use crate::error::InputError;
+use crate::error::ParseError;
+use crate::PResult;
+use crate::Parser;
+
+pub use super::chapter_6 as previous;
+pub use crate::_tutorial as table_of_content;
diff --git a/src/_tutorial/mod.rs b/src/_tutorial/mod.rs
new file mode 100644
index 0000000..e4b8392
--- /dev/null
+++ b/src/_tutorial/mod.rs
@@ -0,0 +1,12 @@
+//! # Tutorial
+//!
+//! Table of Content
+
+pub mod chapter_0;
+pub mod chapter_1;
+pub mod chapter_2;
+pub mod chapter_3;
+pub mod chapter_4;
+pub mod chapter_5;
+pub mod chapter_6;
+pub mod chapter_7;
diff --git a/src/ascii/mod.rs b/src/ascii/mod.rs
new file mode 100644
index 0000000..8b3119f
--- /dev/null
+++ b/src/ascii/mod.rs
@@ -0,0 +1,1675 @@
+//! Character specific parsers and combinators
+//!
+//! Functions recognizing specific characters
+
+#[cfg(test)]
+mod tests;
+
+use crate::lib::std::ops::{Add, Shl};
+
+use crate::combinator::alt;
+use crate::combinator::cut_err;
+use crate::combinator::opt;
+use crate::error::ParserError;
+use crate::error::{ErrMode, ErrorKind, Needed};
+use crate::stream::{AsBStr, AsChar, ParseSlice, Stream, StreamIsPartial};
+use crate::stream::{Compare, CompareResult};
+use crate::token::one_of;
+use crate::token::take_till0;
+use crate::token::take_while;
+use crate::trace::trace;
+use crate::PResult;
+use crate::Parser;
+
+/// Recognizes the string `"\r\n"`.
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}};
+/// # use winnow::ascii::crlf;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// crlf.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n")));
+/// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::crlf;
+/// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n")));
+/// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag))));
+/// assert_eq!(crlf::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn crlf<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+{
+ trace("crlf", "\r\n").parse_next(input)
+}
+
+/// Recognizes a string of any char except `"\r\n"` or `"\n"`.
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::not_line_ending;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// not_line_ending.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("ab\r\nc"), Ok(("\r\nc", "ab")));
+/// assert_eq!(parser.parse_peek("ab\nc"), Ok(("\nc", "ab")));
+/// assert_eq!(parser.parse_peek("abc"), Ok(("", "abc")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// assert_eq!(parser.parse_peek("a\rb\nc"), Err(ErrMode::Backtrack(InputError::new("\rb\nc", ErrorKind::Tag ))));
+/// assert_eq!(parser.parse_peek("a\rbc"), Err(ErrMode::Backtrack(InputError::new("\rbc", ErrorKind::Tag ))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::not_line_ending;
+/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Ok((Partial::new("\r\nc"), "ab")));
+/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("abc")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rb\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rb\nc"), ErrorKind::Tag ))));
+/// assert_eq!(not_line_ending::<_, InputError<_>>.parse_peek(Partial::new("a\rbc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\rbc"), ErrorKind::Tag ))));
+/// ```
+#[inline(always)]
+pub fn not_line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("not_line_ending", move |input: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ not_line_ending_::<_, _, true>(input)
+ } else {
+ not_line_ending_::<_, _, false>(input)
+ }
+ })
+ .parse_next(input)
+}
+
+fn not_line_ending_<I, E: ParserError<I>, const PARTIAL: bool>(
+ input: &mut I,
+) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ let res = take_till0(('\r', '\n')).parse_next(input)?;
+ if input.compare("\r") == CompareResult::Ok {
+ let comp = input.compare("\r\n");
+ match comp {
+ //FIXME: calculate the right index
+ CompareResult::Ok => {}
+ CompareResult::Incomplete if PARTIAL && input.is_partial() => {
+ return Err(ErrMode::Incomplete(Needed::Unknown));
+ }
+ CompareResult::Incomplete | CompareResult::Error => {
+ let e: ErrorKind = ErrorKind::Tag;
+ return Err(ErrMode::from_error_kind(input, e));
+ }
+ }
+ }
+ Ok(res)
+}
+
+/// Recognizes an end of line (both `"\n"` and `"\r\n"`).
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::line_ending;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// line_ending.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("\r\nc"), Ok(("c", "\r\n")));
+/// assert_eq!(parser.parse_peek("ab\r\nc"), Err(ErrMode::Backtrack(InputError::new("ab\r\nc", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::line_ending;
+/// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Ok((Partial::new("c"), "\r\n")));
+/// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("ab\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("ab\r\nc"), ErrorKind::Tag))));
+/// assert_eq!(line_ending::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn line_ending<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+{
+ trace("line_ending", alt(("\n", "\r\n"))).parse_next(input)
+}
+
+/// Matches a newline character `'\n'`.
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::newline;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> {
+/// newline.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("\nc"), Ok(("c", '\n')));
+/// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::newline;
+/// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\nc")), Ok((Partial::new("c"), '\n')));
+/// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify))));
+/// assert_eq!(newline::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn newline<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("newline", '\n'.map(AsChar::as_char)).parse_next(input)
+}
+
+/// Matches a tab character `'\t'`.
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::tab;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<char, InputError<&'s str>> {
+/// tab.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("\tc"), Ok(("c", '\t')));
+/// assert_eq!(parser.parse_peek("\r\nc"), Err(ErrMode::Backtrack(InputError::new("\r\nc", ErrorKind::Verify))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::tab;
+/// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\tc")), Ok((Partial::new("c"), '\t')));
+/// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("\r\nc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("\r\nc"), ErrorKind::Verify))));
+/// assert_eq!(tab::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn tab<I, Error: ParserError<I>>(input: &mut I) -> PResult<char, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("tab", '\t'.map(AsChar::as_char)).parse_next(input)
+}
+
+/// Recognizes zero or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'`
+///
+/// *Complete version*: Will return the whole input if no terminating token is found (a non
+/// alphabetic character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non alphabetic character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::alpha0;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// alpha0.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("ab1c"), Ok(("1c", "ab")));
+/// assert_eq!(parser.parse_peek("1c"), Ok(("1c", "")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::alpha0;
+/// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("ab1c")), Ok((Partial::new("1c"), "ab")));
+/// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("1c")), Ok((Partial::new("1c"), "")));
+/// assert_eq!(alpha0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn alpha0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("alpha0", take_while(0.., AsChar::is_alpha)).parse_next(input)
+}
+
+/// Recognizes one or more lowercase and uppercase ASCII alphabetic characters: `'a'..='z'`, `'A'..='Z'`
+///
+/// *Complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non alphabetic character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non alphabetic character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::alpha1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// alpha1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("aB1c"), Ok(("1c", "aB")));
+/// assert_eq!(parser.parse_peek("1c"), Err(ErrMode::Backtrack(InputError::new("1c", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::alpha1;
+/// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("aB1c")), Ok((Partial::new("1c"), "aB")));
+/// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("1c")), Err(ErrMode::Backtrack(InputError::new(Partial::new("1c"), ErrorKind::Slice))));
+/// assert_eq!(alpha1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn alpha1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("alpha1", take_while(1.., AsChar::is_alpha)).parse_next(input)
+}
+
+/// Recognizes zero or more ASCII numerical characters: `'0'..='9'`
+///
+/// *Complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non digit character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non digit character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::digit0;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// digit0.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21")));
+/// assert_eq!(parser.parse_peek("21"), Ok(("", "21")));
+/// assert_eq!(parser.parse_peek("a21c"), Ok(("a21c", "")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::digit0;
+/// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21")));
+/// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("a21c")), Ok((Partial::new("a21c"), "")));
+/// assert_eq!(digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("digit0", take_while(0.., AsChar::is_dec_digit)).parse_next(input)
+}
+
+/// Recognizes one or more ASCII numerical characters: `'0'..='9'`
+///
+/// *Complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non digit character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non digit character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::digit1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// digit1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21c"), Ok(("c", "21")));
+/// assert_eq!(parser.parse_peek("c1"), Err(ErrMode::Backtrack(InputError::new("c1", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::digit1;
+/// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("21c")), Ok((Partial::new("c"), "21")));
+/// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("c1")), Err(ErrMode::Backtrack(InputError::new(Partial::new("c1"), ErrorKind::Slice))));
+/// assert_eq!(digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+///
+/// ## Parsing an integer
+///
+/// You can use `digit1` in combination with [`Parser::try_map`][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<u32, InputError<&'s str>> {
+/// digit1.try_map(str::parse).parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("416"), Ok(("", 416)));
+/// assert_eq!(parser.parse_peek("12b"), Ok(("b", 12)));
+/// assert!(parser.parse_peek("b").is_err());
+/// ```
+#[inline(always)]
+pub fn digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("digit1", take_while(1.., AsChar::is_dec_digit)).parse_next(input)
+}
+
+/// Recognizes zero or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`,
+/// `'a'..='f'`
+///
+/// *Complete version*: Will return the whole input if no terminating token is found (a non hexadecimal digit character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non hexadecimal digit character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::hex_digit0;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// hex_digit0.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c")));
+/// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", "")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::hex_digit0;
+/// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c")));
+/// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
+/// assert_eq!(hex_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn hex_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("hex_digit0", take_while(0.., AsChar::is_hex_digit)).parse_next(input)
+}
+
+/// Recognizes one or more ASCII hexadecimal numerical characters: `'0'..='9'`, `'A'..='F'`,
+/// `'a'..='f'`
+///
+/// *Complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non hexadecimal digit character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non hexadecimal digit character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::hex_digit1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// hex_digit1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21cZ"), Ok(("Z", "21c")));
+/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::hex_digit1;
+/// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("Z"), "21c")));
+/// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
+/// assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn hex_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("hex_digit1", take_while(1.., AsChar::is_hex_digit)).parse_next(input)
+}
+
+/// Recognizes zero or more octal characters: `'0'..='7'`
+///
+/// *Complete version*: Will return the whole input if no terminating token is found (a non octal
+/// digit character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non octal digit character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::oct_digit0;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// oct_digit0.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21")));
+/// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", "")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::oct_digit0;
+/// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21")));
+/// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
+/// assert_eq!(oct_digit0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn oct_digit0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("oct_digit0", take_while(0.., AsChar::is_oct_digit)).parse_next(input)
+}
+
+/// Recognizes one or more octal characters: `'0'..='7'`
+///
+/// *Complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non octal digit character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non octal digit character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::oct_digit1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// oct_digit1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21cZ"), Ok(("cZ", "21")));
+/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::oct_digit1;
+/// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("21cZ")), Ok((Partial::new("cZ"), "21")));
+/// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
+/// assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn oct_digit1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("oct_digit0", take_while(1.., AsChar::is_oct_digit)).parse_next(input)
+}
+
+/// Recognizes zero or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'`
+///
+/// *Complete version*: Will return the whole input if no terminating token is found (a non
+/// alphanumerical character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non alphanumerical character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::alphanumeric0;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// alphanumeric0.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ")));
+/// assert_eq!(parser.parse_peek("&Z21c"), Ok(("&Z21c", "")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::alphanumeric0;
+/// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ")));
+/// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("&Z21c")), Ok((Partial::new("&Z21c"), "")));
+/// assert_eq!(alphanumeric0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn alphanumeric0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("alphanumeric0", take_while(0.., AsChar::is_alphanum)).parse_next(input)
+}
+
+/// Recognizes one or more ASCII numerical and alphabetic characters: `'a'..='z'`, `'A'..='Z'`, `'0'..='9'`
+///
+/// *Complete version*: Will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non alphanumerical character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non alphanumerical character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::alphanumeric1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// alphanumeric1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("21cZ%1"), Ok(("%1", "21cZ")));
+/// assert_eq!(parser.parse_peek("&H2"), Err(ErrMode::Backtrack(InputError::new("&H2", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::alphanumeric1;
+/// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("21cZ%1")), Ok((Partial::new("%1"), "21cZ")));
+/// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("&H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("&H2"), ErrorKind::Slice))));
+/// assert_eq!(alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn alphanumeric1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar,
+{
+ trace("alphanumeric1", take_while(1.., AsChar::is_alphanum)).parse_next(input)
+}
+
+/// Recognizes zero or more spaces and tabs.
+///
+/// *Complete version*: Will return the whole input if no terminating token is found (a non space
+/// character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non space character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::space0;
+/// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t")));
+/// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
+/// assert_eq!(space0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn space0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("space0", take_while(0.., AsChar::is_space)).parse_next(input)
+}
+
+/// Recognizes zero or more spaces and tabs.
+///
+/// *Complete version*: Will return the whole input if no terminating token is found (a non space
+/// character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non space character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::space1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// space1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek(" \t21c"), Ok(("21c", " \t")));
+/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::space1;
+/// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new(" \t21c")), Ok((Partial::new("21c"), " \t")));
+/// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
+/// assert_eq!(space1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn space1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("space1", take_while(1.., AsChar::is_space)).parse_next(input)
+}
+
+/// Recognizes zero or more spaces, tabs, carriage returns and line feeds.
+///
+/// *Complete version*: will return the whole input if no terminating token is found (a non space
+/// character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non space character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::ascii::multispace0;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// multispace0.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r")));
+/// assert_eq!(parser.parse_peek("Z21c"), Ok(("Z21c", "")));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::multispace0;
+/// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r")));
+/// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("Z21c")), Ok((Partial::new("Z21c"), "")));
+/// assert_eq!(multispace0::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn multispace0<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("multispace0", take_while(0.., (' ', '\t', '\r', '\n'))).parse_next(input)
+}
+
+/// Recognizes one or more spaces, tabs, carriage returns and line feeds.
+///
+/// *Complete version*: will return an error if there's not enough input data,
+/// or the whole input if no terminating token is found (a non space character).
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data,
+/// or if no terminating token is found (a non space character).
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::ascii::multispace1;
+/// fn parser<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// multispace1.parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek(" \t\n\r21c"), Ok(("21c", " \t\n\r")));
+/// assert_eq!(parser.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// # use winnow::ascii::multispace1;
+/// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new(" \t\n\r21c")), Ok((Partial::new("21c"), " \t\n\r")));
+/// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("H2")), Err(ErrMode::Backtrack(InputError::new(Partial::new("H2"), ErrorKind::Slice))));
+/// assert_eq!(multispace1::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn multispace1<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+{
+ trace("multispace1", take_while(1.., (' ', '\t', '\r', '\n'))).parse_next(input)
+}
+
+/// Decode a decimal unsigned integer (e.g. [`u32`])
+///
+/// *Complete version*: can parse until the end of input.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+#[doc(alias = "u8")]
+#[doc(alias = "u16")]
+#[doc(alias = "u32")]
+#[doc(alias = "u64")]
+#[doc(alias = "u128")]
+pub fn dec_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+ O: Uint,
+{
+ trace("dec_uint", move |input: &mut I| {
+ if input.eof_offset() == 0 {
+ if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+ return Err(ErrMode::Incomplete(Needed::new(1)));
+ } else {
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+ }
+ }
+
+ let mut value = O::default();
+ for (offset, c) in input.iter_offsets() {
+ match c.as_char().to_digit(10) {
+ Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
+ let d = d as u8;
+ v.checked_add(d, sealed::SealedMarker)
+ }) {
+ None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)),
+ Some(v) => value = v,
+ },
+ None => {
+ if offset == 0 {
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+ } else {
+ let _ = input.next_slice(offset);
+ return Ok(value);
+ }
+ }
+ }
+ }
+
+ if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ } else {
+ let _ = input.finish();
+ Ok(value)
+ }
+ })
+ .parse_next(input)
+}
+
+/// Metadata for parsing unsigned integers, see [`dec_uint`]
+pub trait Uint: Default {
+ #[doc(hidden)]
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
+ #[doc(hidden)]
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
+}
+
+impl Uint for u8 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for u16 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for u32 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for u64 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for u128 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for i8 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for i16 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for i32 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for i64 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+impl Uint for i128 {
+ fn checked_mul(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_mul(by as Self)
+ }
+ fn checked_add(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_add(by as Self)
+ }
+}
+
+/// Decode a decimal signed integer (e.g. [`i32`])
+///
+/// *Complete version*: can parse until the end of input.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+#[doc(alias = "i8")]
+#[doc(alias = "i16")]
+#[doc(alias = "i32")]
+#[doc(alias = "i64")]
+#[doc(alias = "i128")]
+pub fn dec_int<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+ O: Int,
+{
+ trace("dec_int", move |input: &mut I| {
+ fn sign(token: impl AsChar) -> bool {
+ let token = token.as_char();
+ token == '+' || token == '-'
+ }
+ let sign = opt(crate::token::one_of(sign).map(AsChar::as_char))
+ .map(|c| c != Some('-'))
+ .parse_next(input)?;
+
+ if input.eof_offset() == 0 {
+ if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+ return Err(ErrMode::Incomplete(Needed::new(1)));
+ } else {
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+ }
+ }
+
+ let mut value = O::default();
+ for (offset, c) in input.iter_offsets() {
+ match c.as_char().to_digit(10) {
+ Some(d) => match value.checked_mul(10, sealed::SealedMarker).and_then(|v| {
+ let d = d as u8;
+ if sign {
+ v.checked_add(d, sealed::SealedMarker)
+ } else {
+ v.checked_sub(d, sealed::SealedMarker)
+ }
+ }) {
+ None => return Err(ErrMode::from_error_kind(input, ErrorKind::Verify)),
+ Some(v) => value = v,
+ },
+ None => {
+ if offset == 0 {
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+ } else {
+ let _ = input.next_slice(offset);
+ return Ok(value);
+ }
+ }
+ }
+ }
+
+ if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ } else {
+ let _ = input.finish();
+ Ok(value)
+ }
+ })
+ .parse_next(input)
+}
+
+/// Metadata for parsing signed integers, see [`dec_int`]
+pub trait Int: Uint {
+ #[doc(hidden)]
+ fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self>;
+}
+
+impl Int for i8 {
+ fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_sub(by as Self)
+ }
+}
+
+impl Int for i16 {
+ fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_sub(by as Self)
+ }
+}
+
+impl Int for i32 {
+ fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_sub(by as Self)
+ }
+}
+
+impl Int for i64 {
+ fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_sub(by as Self)
+ }
+}
+
+impl Int for i128 {
+ fn checked_sub(self, by: u8, _: sealed::SealedMarker) -> Option<Self> {
+ self.checked_sub(by as Self)
+ }
+}
+
+/// Decode a variable-width hexadecimal integer (e.g. [`u32`])
+///
+/// *Complete version*: Will parse until the end of input if it has fewer characters than the type
+/// supports.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if end-of-input
+/// is hit before a hard boundary (non-hex character, more characters than supported).
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// use winnow::ascii::hex_uint;
+///
+/// fn parser<'s>(s: &mut &'s [u8]) -> PResult<u32, InputError<&'s [u8]>> {
+/// hex_uint(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek(&b"01AE"[..]), Ok((&b""[..], 0x01AE)));
+/// assert_eq!(parser.parse_peek(&b"abc"[..]), Ok((&b""[..], 0x0ABC)));
+/// assert_eq!(parser.parse_peek(&b"ggg"[..]), Err(ErrMode::Backtrack(InputError::new(&b"ggg"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// use winnow::ascii::hex_uint;
+///
+/// fn parser<'s>(s: &mut Partial<&'s [u8]>) -> PResult<u32, InputError<Partial<&'s [u8]>>> {
+/// hex_uint(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek(Partial::new(&b"01AE;"[..])), Ok((Partial::new(&b";"[..]), 0x01AE)));
+/// assert_eq!(parser.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(parser.parse_peek(Partial::new(&b"ggg"[..])), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"ggg"[..]), ErrorKind::Slice))));
+/// ```
+#[inline]
+pub fn hex_uint<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ O: HexUint,
+ <I as Stream>::Token: AsChar,
+ <I as Stream>::Slice: AsBStr,
+{
+ trace("hex_uint", move |input: &mut I| {
+ let invalid_offset = input
+ .offset_for(|c| {
+ let c = c.as_char();
+ !"0123456789abcdefABCDEF".contains(c)
+ })
+ .unwrap_or_else(|| input.eof_offset());
+ let max_nibbles = O::max_nibbles(sealed::SealedMarker);
+ let max_offset = input.offset_at(max_nibbles);
+ let offset = match max_offset {
+ Ok(max_offset) => {
+ if max_offset < invalid_offset {
+ // Overflow
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Verify));
+ } else {
+ invalid_offset
+ }
+ }
+ Err(_) => {
+ if <I as StreamIsPartial>::is_partial_supported()
+ && input.is_partial()
+ && invalid_offset == input.eof_offset()
+ {
+ // Only the next byte is guaranteed required
+ return Err(ErrMode::Incomplete(Needed::new(1)));
+ } else {
+ invalid_offset
+ }
+ }
+ };
+ if offset == 0 {
+ // Must be at least one digit
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+ }
+ let parsed = input.next_slice(offset);
+
+ let mut res = O::default();
+ for c in parsed.as_bstr() {
+ let nibble = *c as char;
+ let nibble = nibble.to_digit(16).unwrap_or(0) as u8;
+ let nibble = O::from(nibble);
+ res = (res << O::from(4)) + nibble;
+ }
+
+ Ok(res)
+ })
+ .parse_next(input)
+}
+
+/// Metadata for parsing hex numbers, see [`hex_uint`]
+pub trait HexUint:
+ Default + Shl<Self, Output = Self> + Add<Self, Output = Self> + From<u8>
+{
+ #[doc(hidden)]
+ fn max_nibbles(_: sealed::SealedMarker) -> usize;
+}
+
+impl HexUint for u8 {
+ #[inline(always)]
+ fn max_nibbles(_: sealed::SealedMarker) -> usize {
+ 2
+ }
+}
+
+impl HexUint for u16 {
+ #[inline(always)]
+ fn max_nibbles(_: sealed::SealedMarker) -> usize {
+ 4
+ }
+}
+
+impl HexUint for u32 {
+ #[inline(always)]
+ fn max_nibbles(_: sealed::SealedMarker) -> usize {
+ 8
+ }
+}
+
+impl HexUint for u64 {
+ #[inline(always)]
+ fn max_nibbles(_: sealed::SealedMarker) -> usize {
+ 16
+ }
+}
+
+impl HexUint for u128 {
+ #[inline(always)]
+ fn max_nibbles(_: sealed::SealedMarker) -> usize {
+ 32
+ }
+}
+
+/// Recognizes floating point number in text format and returns a [`f32`] or [`f64`].
+///
+/// *Complete version*: Can parse until the end of input.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::error::Needed::Size;
+/// use winnow::ascii::float;
+///
+/// fn parser<'s>(s: &mut &'s str) -> PResult<f64, InputError<&'s str>> {
+/// float(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek("11e-1"), Ok(("", 1.1)));
+/// assert_eq!(parser.parse_peek("123E-02"), Ok(("", 1.23)));
+/// assert_eq!(parser.parse_peek("123K-01"), Ok(("K-01", 123.0)));
+/// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Tag))));
+/// ```
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::ascii::float;
+///
+/// fn parser<'s>(s: &mut Partial<&'s str>) -> PResult<f64, InputError<Partial<&'s str>>> {
+/// float(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek(Partial::new("11e-1 ")), Ok((Partial::new(" "), 1.1)));
+/// assert_eq!(parser.parse_peek(Partial::new("11e-1")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(parser.parse_peek(Partial::new("123E-02")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(parser.parse_peek(Partial::new("123K-01")), Ok((Partial::new("K-01"), 123.0)));
+/// assert_eq!(parser.parse_peek(Partial::new("abc")), Err(ErrMode::Backtrack(InputError::new(Partial::new("abc"), ErrorKind::Tag))));
+/// ```
+#[inline(always)]
+#[doc(alias = "f32")]
+#[doc(alias = "double")]
+#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
+pub fn float<I, O, E: ParserError<I>>(input: &mut I) -> PResult<O, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+ <I as Stream>::Slice: ParseSlice<O>,
+ <I as Stream>::Token: AsChar + Clone,
+ <I as Stream>::IterOffsets: Clone,
+ I: AsBStr,
+{
+ trace("float", move |input: &mut I| {
+ let s = recognize_float_or_exceptions(input)?;
+ s.parse_slice()
+ .ok_or_else(|| ErrMode::from_error_kind(input, ErrorKind::Verify))
+ })
+ .parse_next(input)
+}
+
+#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
+fn recognize_float_or_exceptions<I, E: ParserError<I>>(
+ input: &mut I,
+) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+ <I as Stream>::Token: AsChar + Clone,
+ <I as Stream>::IterOffsets: Clone,
+ I: AsBStr,
+{
+ alt((
+ recognize_float,
+ crate::token::tag_no_case("nan"),
+ crate::token::tag_no_case("infinity"),
+ crate::token::tag_no_case("inf"),
+ ))
+ .parse_next(input)
+}
+
+#[allow(clippy::trait_duplication_in_bounds)] // HACK: clippy 1.64.0 bug
+fn recognize_float<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ I: Compare<&'static str>,
+ <I as Stream>::Token: AsChar + Clone,
+ <I as Stream>::IterOffsets: Clone,
+ I: AsBStr,
+{
+ (
+ opt(one_of(['+', '-'])),
+ alt((
+ (digit1, opt(('.', opt(digit1)))).map(|_| ()),
+ ('.', digit1).map(|_| ()),
+ )),
+ opt((one_of(['e', 'E']), opt(one_of(['+', '-'])), cut_err(digit1))),
+ )
+ .recognize()
+ .parse_next(input)
+}
+
+/// Matches a byte string with escaped characters.
+///
+/// * The first argument matches the normal characters (it must not accept the control character)
+/// * The second argument is the control character (like `\` in most languages)
+/// * The third argument matches the escaped characters
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult};
+/// # use winnow::ascii::digit1;
+/// # use winnow::prelude::*;
+/// use winnow::ascii::escaped;
+/// use winnow::token::one_of;
+///
+/// fn esc(s: &str) -> IResult<&str, &str> {
+/// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
+/// }
+///
+/// assert_eq!(esc("123;"), Ok((";", "123")));
+/// assert_eq!(esc(r#"12\"34;"#), Ok((";", r#"12\"34"#)));
+/// ```
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed, IResult};
+/// # use winnow::ascii::digit1;
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::ascii::escaped;
+/// use winnow::token::one_of;
+///
+/// fn esc(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// escaped(digit1, '\\', one_of(['"', 'n', '\\'])).parse_peek(s)
+/// }
+///
+/// assert_eq!(esc(Partial::new("123;")), Ok((Partial::new(";"), "123")));
+/// assert_eq!(esc(Partial::new("12\\\"34;")), Ok((Partial::new(";"), "12\\\"34")));
+/// ```
+#[inline(always)]
+pub fn escaped<'a, I: 'a, Error, F, G, O1, O2>(
+ mut normal: F,
+ control_char: char,
+ mut escapable: G,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+ F: Parser<I, O1, Error>,
+ G: Parser<I, O2, Error>,
+ Error: ParserError<I>,
+{
+ trace("escaped", move |input: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+ streaming_escaped_internal(input, &mut normal, control_char, &mut escapable)
+ } else {
+ complete_escaped_internal(input, &mut normal, control_char, &mut escapable)
+ }
+ })
+}
+
+fn streaming_escaped_internal<I, Error, F, G, O1, O2>(
+ input: &mut I,
+ normal: &mut F,
+ control_char: char,
+ escapable: &mut G,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+ F: Parser<I, O1, Error>,
+ G: Parser<I, O2, Error>,
+ Error: ParserError<I>,
+{
+ let start = input.checkpoint();
+
+ while input.eof_offset() > 0 {
+ let current_len = input.eof_offset();
+
+ match opt(normal.by_ref()).parse_next(input)? {
+ Some(_) => {
+ if input.eof_offset() == current_len {
+ let offset = input.offset_from(&start);
+ input.reset(start);
+ return Ok(input.next_slice(offset));
+ }
+ }
+ None => {
+ if opt(control_char).parse_next(input)?.is_some() {
+ let _ = escapable.parse_next(input)?;
+ } else {
+ let offset = input.offset_from(&start);
+ input.reset(start);
+ return Ok(input.next_slice(offset));
+ }
+ }
+ }
+ }
+
+ Err(ErrMode::Incomplete(Needed::Unknown))
+}
+
+fn complete_escaped_internal<'a, I: 'a, Error, F, G, O1, O2>(
+ input: &mut I,
+ normal: &mut F,
+ control_char: char,
+ escapable: &mut G,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: crate::stream::AsChar + Clone,
+ F: Parser<I, O1, Error>,
+ G: Parser<I, O2, Error>,
+ Error: ParserError<I>,
+{
+ let start = input.checkpoint();
+
+ while input.eof_offset() > 0 {
+ let current_len = input.eof_offset();
+
+ match opt(normal.by_ref()).parse_next(input)? {
+ Some(_) => {
+ if input.eof_offset() == current_len {
+ let offset = input.offset_from(&start);
+ input.reset(start);
+ return Ok(input.next_slice(offset));
+ }
+ }
+ None => {
+ if opt(control_char).parse_next(input)?.is_some() {
+ let _ = escapable.parse_next(input)?;
+ } else {
+ let offset = input.offset_from(&start);
+ input.reset(start);
+ return Ok(input.next_slice(offset));
+ }
+ }
+ }
+ }
+
+ input.reset(start);
+ Ok(input.finish())
+}
+
+/// Matches a byte string with escaped characters.
+///
+/// * The first argument matches the normal characters (it must not match the control character)
+/// * The second argument is the control character (like `\` in most languages)
+/// * The third argument matches the escaped characters and transforms them
+///
+/// As an example, the chain `abc\tdef` could be `abc def` (it also consumes the control character)
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use std::str::from_utf8;
+/// use winnow::token::tag;
+/// use winnow::ascii::escaped_transform;
+/// use winnow::ascii::alpha1;
+/// use winnow::combinator::alt;
+///
+/// fn parser<'s>(input: &mut &'s str) -> PResult<String, InputError<&'s str>> {
+/// escaped_transform(
+/// alpha1,
+/// '\\',
+/// alt((
+/// "\\".value("\\"),
+/// "\"".value("\""),
+/// "n".value("\n"),
+/// ))
+/// ).parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek("ab\\\"cd"), Ok(("", String::from("ab\"cd"))));
+/// assert_eq!(parser.parse_peek("ab\\ncd"), Ok(("", String::from("ab\ncd"))));
+/// ```
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use std::str::from_utf8;
+/// # use winnow::Partial;
+/// use winnow::token::tag;
+/// use winnow::ascii::escaped_transform;
+/// use winnow::ascii::alpha1;
+/// use winnow::combinator::alt;
+///
+/// fn parser<'s>(input: &mut Partial<&'s str>) -> PResult<String, InputError<Partial<&'s str>>> {
+/// escaped_transform(
+/// alpha1,
+/// '\\',
+/// alt((
+/// "\\".value("\\"),
+/// "\"".value("\""),
+/// "n".value("\n"),
+/// ))
+/// ).parse_next(input)
+/// }
+///
+/// assert_eq!(parser.parse_peek(Partial::new("ab\\\"cd\"")), Ok((Partial::new("\""), String::from("ab\"cd"))));
+/// ```
+#[inline(always)]
+pub fn escaped_transform<I, Error, F, G, Output>(
+ mut normal: F,
+ control_char: char,
+ mut transform: G,
+) -> impl Parser<I, Output, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: crate::stream::AsChar + Clone,
+ Output: crate::stream::Accumulate<<I as Stream>::Slice>,
+ F: Parser<I, <I as Stream>::Slice, Error>,
+ G: Parser<I, <I as Stream>::Slice, Error>,
+ Error: ParserError<I>,
+{
+ trace("escaped_transform", move |input: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() && input.is_partial() {
+ streaming_escaped_transform_internal(input, &mut normal, control_char, &mut transform)
+ } else {
+ complete_escaped_transform_internal(input, &mut normal, control_char, &mut transform)
+ }
+ })
+}
+
+fn streaming_escaped_transform_internal<I, Error, F, G, Output>(
+ input: &mut I,
+ normal: &mut F,
+ control_char: char,
+ transform: &mut G,
+) -> PResult<Output, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: crate::stream::AsChar + Clone,
+ Output: crate::stream::Accumulate<<I as Stream>::Slice>,
+ F: Parser<I, <I as Stream>::Slice, Error>,
+ G: Parser<I, <I as Stream>::Slice, Error>,
+ Error: ParserError<I>,
+{
+ let mut res = Output::initial(Some(input.eof_offset()));
+
+ while input.eof_offset() > 0 {
+ let current_len = input.eof_offset();
+ match opt(normal.by_ref()).parse_next(input)? {
+ Some(o) => {
+ res.accumulate(o);
+ if input.eof_offset() == current_len {
+ return Ok(res);
+ }
+ }
+ None => {
+ if opt(control_char).parse_next(input)?.is_some() {
+ let o = transform.parse_next(input)?;
+ res.accumulate(o);
+ } else {
+ return Ok(res);
+ }
+ }
+ }
+ }
+ Err(ErrMode::Incomplete(Needed::Unknown))
+}
+
+fn complete_escaped_transform_internal<I, Error, F, G, Output>(
+ input: &mut I,
+ normal: &mut F,
+ control_char: char,
+ transform: &mut G,
+) -> PResult<Output, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: crate::stream::AsChar + Clone,
+ Output: crate::stream::Accumulate<<I as Stream>::Slice>,
+ F: Parser<I, <I as Stream>::Slice, Error>,
+ G: Parser<I, <I as Stream>::Slice, Error>,
+ Error: ParserError<I>,
+{
+ let mut res = Output::initial(Some(input.eof_offset()));
+
+ while input.eof_offset() > 0 {
+ let current_len = input.eof_offset();
+
+ match opt(normal.by_ref()).parse_next(input)? {
+ Some(o) => {
+ res.accumulate(o);
+ if input.eof_offset() == current_len {
+ return Ok(res);
+ }
+ }
+ None => {
+ if opt(control_char).parse_next(input)?.is_some() {
+ let o = transform.parse_next(input)?;
+ res.accumulate(o);
+ } else {
+ return Ok(res);
+ }
+ }
+ }
+ }
+ Ok(res)
+}
+
+mod sealed {
+ pub struct SealedMarker;
+}
diff --git a/src/ascii/tests.rs b/src/ascii/tests.rs
new file mode 100644
index 0000000..aacbd86
--- /dev/null
+++ b/src/ascii/tests.rs
@@ -0,0 +1,1554 @@
+use super::*;
+use crate::prelude::*;
+
+mod complete {
+ use super::*;
+ use crate::combinator::alt;
+ use crate::combinator::opt;
+ use crate::error::ErrMode;
+ use crate::error::ErrorKind;
+ use crate::error::InputError;
+ use crate::stream::ParseSlice;
+ use crate::token::none_of;
+ use crate::token::one_of;
+ #[cfg(feature = "alloc")]
+ use crate::{lib::std::string::String, lib::std::vec::Vec};
+ use proptest::prelude::*;
+
+ macro_rules! assert_parse(
+ ($left: expr, $right: expr) => {
+ let res: $crate::IResult<_, _, InputError<_>> = $left;
+ assert_eq!(res, $right);
+ };
+ );
+
+ #[test]
+ fn character() {
+ let empty: &[u8] = b"";
+ let a: &[u8] = b"abcd";
+ let b: &[u8] = b"1234";
+ let c: &[u8] = b"a123";
+ let d: &[u8] = "azé12".as_bytes();
+ let e: &[u8] = b" ";
+ let f: &[u8] = b" ;";
+ //assert_eq!(alpha1::<_, InputError>(a), Err(ErrMode::Incomplete(Needed::Size(1))));
+ assert_parse!(alpha1.parse_peek(a), Ok((empty, a)));
+ assert_eq!(
+ alpha1.parse_peek(b),
+ Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(c),
+ Ok((&c[1..], &b"a"[..]))
+ );
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(d),
+ Ok(("é12".as_bytes(), &b"az"[..]))
+ );
+ assert_eq!(
+ digit1.parse_peek(a),
+ Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice)))
+ );
+ assert_eq!(digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b)));
+ assert_eq!(
+ digit1.parse_peek(c),
+ Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ digit1.parse_peek(d),
+ Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice)))
+ );
+ assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(a), Ok((empty, a)));
+ assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b)));
+ assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(c), Ok((empty, c)));
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(d),
+ Ok(("zé12".as_bytes(), &b"a"[..]))
+ );
+ assert_eq!(
+ hex_digit1.parse_peek(e),
+ Err(ErrMode::Backtrack(InputError::new(e, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(a),
+ Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice)))
+ );
+ assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b)));
+ assert_eq!(
+ oct_digit1.parse_peek(c),
+ Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(d),
+ Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(a),
+ Ok((empty, a))
+ );
+ //assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b)));
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(c),
+ Ok((empty, c))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(d),
+ Ok(("é12".as_bytes(), &b"az"[..]))
+ );
+ assert_eq!(space1::<_, InputError<_>>.parse_peek(e), Ok((empty, e)));
+ assert_eq!(
+ space1::<_, InputError<_>>.parse_peek(f),
+ Ok((&b";"[..], &b" "[..]))
+ );
+ }
+
+ #[cfg(feature = "alloc")]
+ #[test]
+ fn character_s() {
+ let empty = "";
+ let a = "abcd";
+ let b = "1234";
+ let c = "a123";
+ let d = "azé12";
+ let e = " ";
+ assert_eq!(alpha1::<_, InputError<_>>.parse_peek(a), Ok((empty, a)));
+ assert_eq!(
+ alpha1.parse_peek(b),
+ Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Slice)))
+ );
+ assert_eq!(alpha1::<_, InputError<_>>.parse_peek(c), Ok((&c[1..], "a")));
+ assert_eq!(alpha1::<_, InputError<_>>.parse_peek(d), Ok(("é12", "az")));
+ assert_eq!(
+ digit1.parse_peek(a),
+ Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice)))
+ );
+ assert_eq!(digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b)));
+ assert_eq!(
+ digit1.parse_peek(c),
+ Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ digit1.parse_peek(d),
+ Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice)))
+ );
+ assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(a), Ok((empty, a)));
+ assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b)));
+ assert_eq!(hex_digit1::<_, InputError<_>>.parse_peek(c), Ok((empty, c)));
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(d),
+ Ok(("zé12", "a"))
+ );
+ assert_eq!(
+ hex_digit1.parse_peek(e),
+ Err(ErrMode::Backtrack(InputError::new(e, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(a),
+ Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Slice)))
+ );
+ assert_eq!(oct_digit1::<_, InputError<_>>.parse_peek(b), Ok((empty, b)));
+ assert_eq!(
+ oct_digit1.parse_peek(c),
+ Err(ErrMode::Backtrack(InputError::new(c, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(d),
+ Err(ErrMode::Backtrack(InputError::new(d, ErrorKind::Slice)))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(a),
+ Ok((empty, a))
+ );
+ //assert_eq!(fix_error!(b,(), alphanumeric), Ok((empty, b)));
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(c),
+ Ok((empty, c))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(d),
+ Ok(("é12", "az"))
+ );
+ assert_eq!(space1::<_, InputError<_>>.parse_peek(e), Ok((empty, e)));
+ }
+
+ use crate::stream::Offset;
+ #[test]
+ fn offset() {
+ let a = &b"abcd;"[..];
+ let b = &b"1234;"[..];
+ let c = &b"a123;"[..];
+ let d = &b" \t;"[..];
+ let e = &b" \t\r\n;"[..];
+ let f = &b"123abcDEF;"[..];
+
+ match alpha1::<_, InputError<_>>.parse_peek(a) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&a) + i.len(), a.len());
+ }
+ _ => panic!("wrong return type in offset test for alpha"),
+ }
+ match digit1::<_, InputError<_>>.parse_peek(b) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&b) + i.len(), b.len());
+ }
+ _ => panic!("wrong return type in offset test for digit"),
+ }
+ match alphanumeric1::<_, InputError<_>>.parse_peek(c) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&c) + i.len(), c.len());
+ }
+ _ => panic!("wrong return type in offset test for alphanumeric"),
+ }
+ match space1::<_, InputError<_>>.parse_peek(d) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&d) + i.len(), d.len());
+ }
+ _ => panic!("wrong return type in offset test for space"),
+ }
+ match multispace1::<_, InputError<_>>.parse_peek(e) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&e) + i.len(), e.len());
+ }
+ _ => panic!("wrong return type in offset test for multispace"),
+ }
+ match hex_digit1::<_, InputError<_>>.parse_peek(f) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&f) + i.len(), f.len());
+ }
+ _ => panic!("wrong return type in offset test for hex_digit"),
+ }
+ match oct_digit1::<_, InputError<_>>.parse_peek(f) {
+ Ok((i, _)) => {
+ assert_eq!(i.offset_from(&f) + i.len(), f.len());
+ }
+ _ => panic!("wrong return type in offset test for oct_digit"),
+ }
+ }
+
+ #[test]
+ fn is_not_line_ending_bytes() {
+ let a: &[u8] = b"ab12cd\nefgh";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(a),
+ Ok((&b"\nefgh"[..], &b"ab12cd"[..]))
+ );
+
+ let b: &[u8] = b"ab12cd\nefgh\nijkl";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(b),
+ Ok((&b"\nefgh\nijkl"[..], &b"ab12cd"[..]))
+ );
+
+ let c: &[u8] = b"ab12cd\r\nefgh\nijkl";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(c),
+ Ok((&b"\r\nefgh\nijkl"[..], &b"ab12cd"[..]))
+ );
+
+ let d: &[u8] = b"ab12cd";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(d),
+ Ok((&[][..], d))
+ );
+ }
+
+ #[test]
+ fn is_not_line_ending_str() {
+ let f = "βèƒôřè\rÂßÇáƒƭèř";
+ assert_eq!(
+ not_line_ending.parse_peek(f),
+ Err(ErrMode::Backtrack(InputError::new(
+ &f[12..],
+ ErrorKind::Tag
+ )))
+ );
+
+ let g2: &str = "ab12cd";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(g2),
+ Ok(("", g2))
+ );
+ }
+
+ #[test]
+ fn hex_digit_test() {
+ let i = &b"0123456789abcdefABCDEF;"[..];
+ assert_parse!(hex_digit1.parse_peek(i), Ok((&b";"[..], &i[..i.len() - 1])));
+
+ let i = &b"g"[..];
+ assert_parse!(
+ hex_digit1.parse_peek(i),
+ Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice)))
+ );
+
+ let i = &b"G"[..];
+ assert_parse!(
+ hex_digit1.parse_peek(i),
+ Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice)))
+ );
+
+ assert!(AsChar::is_hex_digit(b'0'));
+ assert!(AsChar::is_hex_digit(b'9'));
+ assert!(AsChar::is_hex_digit(b'a'));
+ assert!(AsChar::is_hex_digit(b'f'));
+ assert!(AsChar::is_hex_digit(b'A'));
+ assert!(AsChar::is_hex_digit(b'F'));
+ assert!(!AsChar::is_hex_digit(b'g'));
+ assert!(!AsChar::is_hex_digit(b'G'));
+ assert!(!AsChar::is_hex_digit(b'/'));
+ assert!(!AsChar::is_hex_digit(b':'));
+ assert!(!AsChar::is_hex_digit(b'@'));
+ assert!(!AsChar::is_hex_digit(b'\x60'));
+ }
+
+ #[test]
+ fn oct_digit_test() {
+ let i = &b"01234567;"[..];
+ assert_parse!(oct_digit1.parse_peek(i), Ok((&b";"[..], &i[..i.len() - 1])));
+
+ let i = &b"8"[..];
+ assert_parse!(
+ oct_digit1.parse_peek(i),
+ Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Slice)))
+ );
+
+ assert!(AsChar::is_oct_digit(b'0'));
+ assert!(AsChar::is_oct_digit(b'7'));
+ assert!(!AsChar::is_oct_digit(b'8'));
+ assert!(!AsChar::is_oct_digit(b'9'));
+ assert!(!AsChar::is_oct_digit(b'a'));
+ assert!(!AsChar::is_oct_digit(b'A'));
+ assert!(!AsChar::is_oct_digit(b'/'));
+ assert!(!AsChar::is_oct_digit(b':'));
+ assert!(!AsChar::is_oct_digit(b'@'));
+ assert!(!AsChar::is_oct_digit(b'\x60'));
+ }
+
+ #[test]
+ fn full_line_windows() {
+ fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
+ (not_line_ending, line_ending).parse_peek(i)
+ }
+ let input = b"abc\r\n";
+ let output = take_full_line(input);
+ assert_eq!(output, Ok((&b""[..], (&b"abc"[..], &b"\r\n"[..]))));
+ }
+
+ #[test]
+ fn full_line_unix() {
+ fn take_full_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
+ (not_line_ending, line_ending).parse_peek(i)
+ }
+ let input = b"abc\n";
+ let output = take_full_line(input);
+ assert_eq!(output, Ok((&b""[..], (&b"abc"[..], &b"\n"[..]))));
+ }
+
+ #[test]
+ fn check_windows_lineending() {
+ let input = b"\r\n";
+ let output = line_ending.parse_peek(&input[..]);
+ assert_parse!(output, Ok((&b""[..], &b"\r\n"[..])));
+ }
+
+ #[test]
+ fn check_unix_lineending() {
+ let input = b"\n";
+ let output = line_ending.parse_peek(&input[..]);
+ assert_parse!(output, Ok((&b""[..], &b"\n"[..])));
+ }
+
+ #[test]
+ fn cr_lf() {
+ assert_parse!(
+ crlf.parse_peek(&b"\r\na"[..]),
+ Ok((&b"a"[..], &b"\r\n"[..]))
+ );
+ assert_parse!(
+ crlf.parse_peek(&b"\r"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"\r"[..],
+ ErrorKind::Tag
+ )))
+ );
+ assert_parse!(
+ crlf.parse_peek(&b"\ra"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"\ra"[..],
+ ErrorKind::Tag
+ )))
+ );
+
+ assert_parse!(crlf.parse_peek("\r\na"), Ok(("a", "\r\n")));
+ assert_parse!(
+ crlf.parse_peek("\r"),
+ Err(ErrMode::Backtrack(error_position!(&"\r", ErrorKind::Tag)))
+ );
+ assert_parse!(
+ crlf.parse_peek("\ra"),
+ Err(ErrMode::Backtrack(error_position!(&"\ra", ErrorKind::Tag)))
+ );
+ }
+
+ #[test]
+ fn end_of_line() {
+ assert_parse!(
+ line_ending.parse_peek(&b"\na"[..]),
+ Ok((&b"a"[..], &b"\n"[..]))
+ );
+ assert_parse!(
+ line_ending.parse_peek(&b"\r\na"[..]),
+ Ok((&b"a"[..], &b"\r\n"[..]))
+ );
+ assert_parse!(
+ line_ending.parse_peek(&b"\r"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"\r"[..],
+ ErrorKind::Tag
+ )))
+ );
+ assert_parse!(
+ line_ending.parse_peek(&b"\ra"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"\ra"[..],
+ ErrorKind::Tag
+ )))
+ );
+
+ assert_parse!(line_ending.parse_peek("\na"), Ok(("a", "\n")));
+ assert_parse!(line_ending.parse_peek("\r\na"), Ok(("a", "\r\n")));
+ assert_parse!(
+ line_ending.parse_peek("\r"),
+ Err(ErrMode::Backtrack(error_position!(&"\r", ErrorKind::Tag)))
+ );
+ assert_parse!(
+ line_ending.parse_peek("\ra"),
+ Err(ErrMode::Backtrack(error_position!(&"\ra", ErrorKind::Tag)))
+ );
+ }
+
+ fn digit_to_i16(input: &str) -> IResult<&str, i16> {
+ let i = input;
+ let (i, opt_sign) = opt(alt(('+', '-'))).parse_peek(i)?;
+ let sign = match opt_sign {
+ Some('+') | None => true,
+ Some('-') => false,
+ _ => unreachable!(),
+ };
+
+ let (i, s) = digit1::<_, InputError<_>>.parse_peek(i)?;
+ match s.parse_slice() {
+ Some(n) => {
+ if sign {
+ Ok((i, n))
+ } else {
+ Ok((i, -n))
+ }
+ }
+ None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
+ }
+ }
+
+ fn digit_to_u32(i: &str) -> IResult<&str, u32> {
+ let (i, s) = digit1.parse_peek(i)?;
+ match s.parse_slice() {
+ Some(n) => Ok((i, n)),
+ None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
+ }
+ }
+
+ proptest! {
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn ints(s in "\\PC*") {
+ let res1 = digit_to_i16(&s);
+ let res2 = dec_int.parse_peek(s.as_str());
+ assert_eq!(res1, res2);
+ }
+
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn uints(s in "\\PC*") {
+ let res1 = digit_to_u32(&s);
+ let res2 = dec_uint.parse_peek(s.as_str());
+ assert_eq!(res1, res2);
+ }
+ }
+
+ #[test]
+ fn hex_uint_tests() {
+ fn hex_u32(input: &[u8]) -> IResult<&[u8], u32> {
+ hex_uint.parse_peek(input)
+ }
+
+ assert_parse!(
+ hex_u32(&b";"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b";"[..],
+ ErrorKind::Slice
+ )))
+ );
+ assert_parse!(hex_u32(&b"ff;"[..]), Ok((&b";"[..], 255)));
+ assert_parse!(hex_u32(&b"1be2;"[..]), Ok((&b";"[..], 7138)));
+ assert_parse!(hex_u32(&b"c5a31be2;"[..]), Ok((&b";"[..], 3_315_801_058)));
+ assert_parse!(hex_u32(&b"C5A31be2;"[..]), Ok((&b";"[..], 3_315_801_058)));
+ assert_parse!(
+ hex_u32(&b"00c5a31be2;"[..]), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"00c5a31be2;"[..],
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ hex_u32(&b"c5a31be201;"[..]), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"c5a31be201;"[..],
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(hex_u32(&b"ffffffff;"[..]), Ok((&b";"[..], 4_294_967_295)));
+ assert_parse!(
+ hex_u32(&b"ffffffffffffffff;"[..]), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"ffffffffffffffff;"[..],
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ hex_u32(&b"ffffffffffffffff"[..]), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"ffffffffffffffff"[..],
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(hex_u32(&b"0x1be2;"[..]), Ok((&b"x1be2;"[..], 0)));
+ assert_parse!(hex_u32(&b"12af"[..]), Ok((&b""[..], 0x12af)));
+ }
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn float_test() {
+ let mut test_cases = vec![
+ "+3.14",
+ "3.14",
+ "-3.14",
+ "0",
+ "0.0",
+ "1.",
+ ".789",
+ "-.5",
+ "1e7",
+ "-1E-7",
+ ".3e-2",
+ "1.e4",
+ "1.2e4",
+ "12.34",
+ "-1.234E-12",
+ "-1.234e-12",
+ "0.00000000000000000087",
+ ];
+
+ for test in test_cases.drain(..) {
+ let expected32 = str::parse::<f32>(test).unwrap();
+ let expected64 = str::parse::<f64>(test).unwrap();
+
+ println!("now parsing: {} -> {}", test, expected32);
+
+ let larger = test.to_string();
+
+ assert_parse!(
+ float.parse_peek(larger.as_bytes()),
+ Ok((&b""[..], expected32))
+ );
+ assert_parse!(float.parse_peek(&larger[..]), Ok(("", expected32)));
+
+ assert_parse!(
+ float.parse_peek(larger.as_bytes()),
+ Ok((&b""[..], expected64))
+ );
+ assert_parse!(float.parse_peek(&larger[..]), Ok(("", expected64)));
+ }
+
+ let remaining_exponent = "-1.234E-";
+ assert_parse!(
+ float::<_, f64, _>.parse_peek(remaining_exponent),
+ Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice)))
+ );
+
+ let (i, nan) = float::<_, f32, ()>.parse_peek("NaN").unwrap();
+ assert!(nan.is_nan());
+ assert_eq!(i, "");
+
+ let (i, inf) = float::<_, f32, ()>.parse_peek("inf").unwrap();
+ assert!(inf.is_infinite());
+ assert_eq!(i, "");
+ let (i, inf) = float::<_, f32, ()>.parse_peek("infinity").unwrap();
+ assert!(inf.is_infinite());
+ assert_eq!(i, "");
+ }
+
+ #[cfg(feature = "std")]
+ fn parse_f64(i: &str) -> IResult<&str, f64, ()> {
+ match super::recognize_float_or_exceptions.parse_peek(i) {
+ Err(e) => Err(e),
+ Ok((i, s)) => {
+ if s.is_empty() {
+ return Err(ErrMode::Backtrack(()));
+ }
+ match s.parse_slice() {
+ Some(n) => Ok((i, n)),
+ None => Err(ErrMode::Backtrack(())),
+ }
+ }
+ }
+ }
+
+ proptest! {
+ #[test]
+ #[cfg(feature = "std")]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn floats(s in "\\PC*") {
+ println!("testing {}", s);
+ let res1 = parse_f64(&s);
+ let res2 = float::<_, f64, ()>.parse_peek(s.as_str());
+ assert_eq!(res1, res2);
+ }
+ }
+
+ // issue #1336 "escaped hangs if normal parser accepts empty"
+ #[test]
+ fn complete_escaped_hang() {
+ // issue #1336 "escaped hangs if normal parser accepts empty"
+ fn escaped_string(input: &str) -> IResult<&str, &str> {
+ use crate::ascii::alpha0;
+ use crate::token::one_of;
+ escaped(alpha0, '\\', one_of(['n'])).parse_peek(input)
+ }
+
+ escaped_string("7").unwrap();
+ escaped_string("a7").unwrap();
+ }
+
+ #[test]
+ fn complete_escaped_hang_1118() {
+ // issue ##1118 escaped does not work with empty string
+ fn unquote(input: &str) -> IResult<&str, &str> {
+ use crate::combinator::delimited;
+ use crate::combinator::opt;
+ use crate::token::one_of;
+
+ delimited(
+ '"',
+ escaped(
+ opt(none_of(['\\', '"'])),
+ '\\',
+ one_of(['\\', '"', 'r', 'n', 't']),
+ ),
+ '"',
+ )
+ .parse_peek(input)
+ }
+
+ assert_eq!(unquote(r#""""#), Ok(("", "")));
+ }
+
+ #[cfg(feature = "alloc")]
+ #[allow(unused_variables)]
+ #[test]
+ fn complete_escaping() {
+ use crate::ascii::{alpha1 as alpha, digit1 as digit};
+ use crate::token::one_of;
+
+ fn esc(i: &[u8]) -> IResult<&[u8], &[u8]> {
+ escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+ }
+ assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], &b"abcd"[..])));
+ assert_eq!(esc(&b"ab\\\"cd;"[..]), Ok((&b";"[..], &b"ab\\\"cd"[..])));
+ assert_eq!(esc(&b"\\\"abcd;"[..]), Ok((&b";"[..], &b"\\\"abcd"[..])));
+ assert_eq!(esc(&b"\\n;"[..]), Ok((&b";"[..], &b"\\n"[..])));
+ assert_eq!(esc(&b"ab\\\"12"[..]), Ok((&b"12"[..], &b"ab\\\""[..])));
+ assert_eq!(
+ esc(&b"AB\\"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b""[..],
+ ErrorKind::Token
+ )))
+ );
+ assert_eq!(
+ esc(&b"AB\\A"[..]),
+ Err(ErrMode::Backtrack(error_node_position!(
+ &&b"AB\\A"[..],
+ ErrorKind::Token,
+ error_position!(&&b"A"[..], ErrorKind::Verify)
+ )))
+ );
+
+ fn esc2(i: &[u8]) -> IResult<&[u8], &[u8]> {
+ escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+ }
+ assert_eq!(esc2(&b"12\\nnn34"[..]), Ok((&b"nn34"[..], &b"12\\n"[..])));
+ }
+
+ #[cfg(feature = "alloc")]
+ #[test]
+ fn complete_escaping_str() {
+ use crate::ascii::{alpha1 as alpha, digit1 as digit};
+ use crate::token::one_of;
+
+ fn esc(i: &str) -> IResult<&str, &str> {
+ escaped(alpha, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+ }
+ assert_eq!(esc("abcd;"), Ok((";", "abcd")));
+ assert_eq!(esc("ab\\\"cd;"), Ok((";", "ab\\\"cd")));
+ assert_eq!(esc("\\\"abcd;"), Ok((";", "\\\"abcd")));
+ assert_eq!(esc("\\n;"), Ok((";", "\\n")));
+ assert_eq!(esc("ab\\\"12"), Ok(("12", "ab\\\"")));
+ assert_eq!(
+ esc("AB\\"),
+ Err(ErrMode::Backtrack(error_position!(&"", ErrorKind::Token)))
+ );
+ assert_eq!(
+ esc("AB\\A"),
+ Err(ErrMode::Backtrack(error_node_position!(
+ &"AB\\A",
+ ErrorKind::Token,
+ error_position!(&"A", ErrorKind::Verify)
+ )))
+ );
+
+ fn esc2(i: &str) -> IResult<&str, &str> {
+ escaped(digit, '\\', one_of(['\"', 'n', '\\'])).parse_peek(i)
+ }
+ assert_eq!(esc2("12\\nnn34"), Ok(("nn34", "12\\n")));
+
+ fn esc3(i: &str) -> IResult<&str, &str> {
+ escaped(alpha, '\u{241b}', one_of(['\"', 'n'])).parse_peek(i)
+ }
+ assert_eq!(esc3("ab␛ncd;"), Ok((";", "ab␛ncd")));
+ }
+
+ #[test]
+ fn test_escaped_error() {
+ fn esc(s: &str) -> IResult<&str, &str> {
+ use crate::ascii::digit1;
+ escaped(digit1, '\\', one_of(['\"', 'n', '\\'])).parse_peek(s)
+ }
+
+ assert_eq!(esc("abcd"), Ok(("abcd", "")));
+ }
+
+ #[cfg(feature = "alloc")]
+ #[test]
+ fn complete_escape_transform() {
+ use crate::ascii::alpha1 as alpha;
+
+ #[cfg(feature = "alloc")]
+ fn to_s(i: Vec<u8>) -> String {
+ String::from_utf8_lossy(&i).into_owned()
+ }
+
+ fn esc(i: &[u8]) -> IResult<&[u8], String> {
+ escaped_transform(
+ alpha,
+ '\\',
+ alt((
+ "\\".value(&b"\\"[..]),
+ "\"".value(&b"\""[..]),
+ "n".value(&b"\n"[..]),
+ )),
+ )
+ .map(to_s)
+ .parse_peek(i)
+ }
+
+ assert_eq!(esc(&b"abcd;"[..]), Ok((&b";"[..], String::from("abcd"))));
+ assert_eq!(
+ esc(&b"ab\\\"cd;"[..]),
+ Ok((&b";"[..], String::from("ab\"cd")))
+ );
+ assert_eq!(
+ esc(&b"\\\"abcd;"[..]),
+ Ok((&b";"[..], String::from("\"abcd")))
+ );
+ assert_eq!(esc(&b"\\n;"[..]), Ok((&b";"[..], String::from("\n"))));
+ assert_eq!(
+ esc(&b"ab\\\"12"[..]),
+ Ok((&b"12"[..], String::from("ab\"")))
+ );
+ assert_eq!(
+ esc(&b"AB\\"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b""[..],
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ esc(&b"AB\\A"[..]),
+ Err(ErrMode::Backtrack(error_node_position!(
+ &&b"AB\\A"[..],
+ ErrorKind::Eof,
+ error_position!(&&b"A"[..], ErrorKind::Tag)
+ )))
+ );
+
+ fn esc2(i: &[u8]) -> IResult<&[u8], String> {
+ escaped_transform(
+ alpha,
+ '&',
+ alt((
+ "egrave;".value("è".as_bytes()),
+ "agrave;".value("à".as_bytes()),
+ )),
+ )
+ .map(to_s)
+ .parse_peek(i)
+ }
+ assert_eq!(
+ esc2(&b"ab&egrave;DEF;"[..]),
+ Ok((&b";"[..], String::from("abèDEF")))
+ );
+ assert_eq!(
+ esc2(&b"ab&egrave;D&agrave;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&egrave;DEF;"), Ok((";", String::from("abèDEF"))));
+ assert_eq!(
+ esc2("ab&egrave;D&agrave;EF;"),
+ Ok((";", String::from("abèDàEF")))
+ );
+
+ fn esc3(i: &str) -> IResult<&str, String> {
+ escaped_transform(alpha, '␛', alt(("0".value("\0"), "n".value("\n")))).parse_peek(i)
+ }
+ assert_eq!(esc3("a␛0bc␛n"), Ok(("", String::from("a\0bc\n"))));
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_escaped_transform_error() {
+ fn esc_trans(s: &str) -> IResult<&str, String> {
+ use crate::ascii::digit1;
+ escaped_transform(digit1, '\\', "n").parse_peek(s)
+ }
+
+ assert_eq!(esc_trans("abcd"), Ok(("abcd", String::new())));
+ }
+}
+
+mod partial {
+ use super::*;
+ use crate::combinator::opt;
+ use crate::error::ErrorKind;
+ use crate::error::InputError;
+ use crate::error::{ErrMode, Needed};
+ use crate::stream::ParseSlice;
+ use crate::IResult;
+ use crate::Partial;
+ use proptest::prelude::*;
+
+ macro_rules! assert_parse(
+ ($left: expr, $right: expr) => {
+ let res: $crate::IResult<_, _, InputError<_>> = $left;
+ assert_eq!(res, $right);
+ };
+ );
+
+ #[test]
+ fn character() {
+ let a: &[u8] = b"abcd";
+ let b: &[u8] = b"1234";
+ let c: &[u8] = b"a123";
+ let d: &[u8] = "azé12".as_bytes();
+ let e: &[u8] = b" ";
+ let f: &[u8] = b" ;";
+ //assert_eq!(alpha1::<_, Error<_>>(a), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_parse!(
+ alpha1.parse_peek(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ alpha1.parse_peek(Partial::new(b)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(b),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Ok((Partial::new(&c[1..]), &b"a"[..]))
+ );
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Ok((Partial::new("é12".as_bytes()), &b"az"[..]))
+ );
+ assert_eq!(
+ digit1.parse_peek(Partial::new(a)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(a),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ digit1::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ digit1.parse_peek(Partial::new(c)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(c),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ digit1.parse_peek(Partial::new(d)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(d),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Ok((Partial::new("zé12".as_bytes()), &b"a"[..]))
+ );
+ assert_eq!(
+ hex_digit1.parse_peek(Partial::new(e)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(e),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(Partial::new(a)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(a),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(Partial::new(c)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(c),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(Partial::new(d)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(d),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ //assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b)));
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Ok((Partial::new("é12".as_bytes()), &b"az"[..]))
+ );
+ assert_eq!(
+ space1::<_, InputError<_>>.parse_peek(Partial::new(e)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ space1::<_, InputError<_>>.parse_peek(Partial::new(f)),
+ Ok((Partial::new(&b";"[..]), &b" "[..]))
+ );
+ }
+
+ #[cfg(feature = "alloc")]
+ #[test]
+ fn character_s() {
+ let a = "abcd";
+ let b = "1234";
+ let c = "a123";
+ let d = "azé12";
+ let e = " ";
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ alpha1.parse_peek(Partial::new(b)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(b),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Ok((Partial::new(&c[1..]), "a"))
+ );
+ assert_eq!(
+ alpha1::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Ok((Partial::new("é12"), "az"))
+ );
+ assert_eq!(
+ digit1.parse_peek(Partial::new(a)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(a),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ digit1::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ digit1.parse_peek(Partial::new(c)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(c),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ digit1.parse_peek(Partial::new(d)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(d),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Ok((Partial::new("zé12"), "a"))
+ );
+ assert_eq!(
+ hex_digit1.parse_peek(Partial::new(e)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(e),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(Partial::new(a)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(a),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(Partial::new(c)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(c),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ oct_digit1.parse_peek(Partial::new(d)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(d),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ //assert_eq!(fix_error!(b,(), alphanumeric1), Ok((empty, b)));
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Ok((Partial::new("é12"), "az"))
+ );
+ assert_eq!(
+ space1::<_, InputError<_>>.parse_peek(Partial::new(e)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ use crate::stream::Offset;
+ #[test]
+ fn offset() {
+ let a = &b"abcd;"[..];
+ let b = &b"1234;"[..];
+ let c = &b"a123;"[..];
+ let d = &b" \t;"[..];
+ let e = &b" \t\r\n;"[..];
+ let f = &b"123abcDEF;"[..];
+
+ match alpha1::<_, InputError<_>>.parse_peek(Partial::new(a)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&a) + i.len(), a.len());
+ }
+ _ => panic!("wrong return type in offset test for alpha"),
+ }
+ match digit1::<_, InputError<_>>.parse_peek(Partial::new(b)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&b) + i.len(), b.len());
+ }
+ _ => panic!("wrong return type in offset test for digit"),
+ }
+ match alphanumeric1::<_, InputError<_>>.parse_peek(Partial::new(c)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&c) + i.len(), c.len());
+ }
+ _ => panic!("wrong return type in offset test for alphanumeric"),
+ }
+ match space1::<_, InputError<_>>.parse_peek(Partial::new(d)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&d) + i.len(), d.len());
+ }
+ _ => panic!("wrong return type in offset test for space"),
+ }
+ match multispace1::<_, InputError<_>>.parse_peek(Partial::new(e)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&e) + i.len(), e.len());
+ }
+ _ => panic!("wrong return type in offset test for multispace"),
+ }
+ match hex_digit1::<_, InputError<_>>.parse_peek(Partial::new(f)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&f) + i.len(), f.len());
+ }
+ _ => panic!("wrong return type in offset test for hex_digit"),
+ }
+ match oct_digit1::<_, InputError<_>>.parse_peek(Partial::new(f)) {
+ Ok((i, _)) => {
+ let i = i.into_inner();
+ assert_eq!(i.offset_from(&f) + i.len(), f.len());
+ }
+ _ => panic!("wrong return type in offset test for oct_digit"),
+ }
+ }
+
+ #[test]
+ fn is_not_line_ending_bytes() {
+ let a: &[u8] = b"ab12cd\nefgh";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(a)),
+ Ok((Partial::new(&b"\nefgh"[..]), &b"ab12cd"[..]))
+ );
+
+ let b: &[u8] = b"ab12cd\nefgh\nijkl";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(b)),
+ Ok((Partial::new(&b"\nefgh\nijkl"[..]), &b"ab12cd"[..]))
+ );
+
+ let c: &[u8] = b"ab12cd\r\nefgh\nijkl";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(c)),
+ Ok((Partial::new(&b"\r\nefgh\nijkl"[..]), &b"ab12cd"[..]))
+ );
+
+ let d: &[u8] = b"ab12cd";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(d)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn is_not_line_ending_str() {
+ let f = "βèƒôřè\rÂßÇáƒƭèř";
+ assert_eq!(
+ not_line_ending.parse_peek(Partial::new(f)),
+ Err(ErrMode::Backtrack(InputError::new(
+ Partial::new(&f[12..]),
+ ErrorKind::Tag
+ )))
+ );
+
+ let g2: &str = "ab12cd";
+ assert_eq!(
+ not_line_ending::<_, InputError<_>>.parse_peek(Partial::new(g2)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn hex_digit_test() {
+ let i = &b"0123456789abcdefABCDEF;"[..];
+ assert_parse!(
+ hex_digit1.parse_peek(Partial::new(i)),
+ Ok((Partial::new(&b";"[..]), &i[..i.len() - 1]))
+ );
+
+ let i = &b"g"[..];
+ assert_parse!(
+ hex_digit1.parse_peek(Partial::new(i)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(i),
+ ErrorKind::Slice
+ )))
+ );
+
+ let i = &b"G"[..];
+ assert_parse!(
+ hex_digit1.parse_peek(Partial::new(i)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(i),
+ ErrorKind::Slice
+ )))
+ );
+
+ assert!(AsChar::is_hex_digit(b'0'));
+ assert!(AsChar::is_hex_digit(b'9'));
+ assert!(AsChar::is_hex_digit(b'a'));
+ assert!(AsChar::is_hex_digit(b'f'));
+ assert!(AsChar::is_hex_digit(b'A'));
+ assert!(AsChar::is_hex_digit(b'F'));
+ assert!(!AsChar::is_hex_digit(b'g'));
+ assert!(!AsChar::is_hex_digit(b'G'));
+ assert!(!AsChar::is_hex_digit(b'/'));
+ assert!(!AsChar::is_hex_digit(b':'));
+ assert!(!AsChar::is_hex_digit(b'@'));
+ assert!(!AsChar::is_hex_digit(b'\x60'));
+ }
+
+ #[test]
+ fn oct_digit_test() {
+ let i = &b"01234567;"[..];
+ assert_parse!(
+ oct_digit1.parse_peek(Partial::new(i)),
+ Ok((Partial::new(&b";"[..]), &i[..i.len() - 1]))
+ );
+
+ let i = &b"8"[..];
+ assert_parse!(
+ oct_digit1.parse_peek(Partial::new(i)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(i),
+ ErrorKind::Slice
+ )))
+ );
+
+ assert!(AsChar::is_oct_digit(b'0'));
+ assert!(AsChar::is_oct_digit(b'7'));
+ assert!(!AsChar::is_oct_digit(b'8'));
+ assert!(!AsChar::is_oct_digit(b'9'));
+ assert!(!AsChar::is_oct_digit(b'a'));
+ assert!(!AsChar::is_oct_digit(b'A'));
+ assert!(!AsChar::is_oct_digit(b'/'));
+ assert!(!AsChar::is_oct_digit(b':'));
+ assert!(!AsChar::is_oct_digit(b'@'));
+ assert!(!AsChar::is_oct_digit(b'\x60'));
+ }
+
+ #[test]
+ fn full_line_windows() {
+ #[allow(clippy::type_complexity)]
+ fn take_full_line(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8])> {
+ (not_line_ending, line_ending).parse_peek(i)
+ }
+ let input = b"abc\r\n";
+ let output = take_full_line(Partial::new(input));
+ assert_eq!(
+ output,
+ Ok((Partial::new(&b""[..]), (&b"abc"[..], &b"\r\n"[..])))
+ );
+ }
+
+ #[test]
+ fn full_line_unix() {
+ #[allow(clippy::type_complexity)]
+ fn take_full_line(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8])> {
+ (not_line_ending, line_ending).parse_peek(i)
+ }
+ let input = b"abc\n";
+ let output = take_full_line(Partial::new(input));
+ assert_eq!(
+ output,
+ Ok((Partial::new(&b""[..]), (&b"abc"[..], &b"\n"[..])))
+ );
+ }
+
+ #[test]
+ fn check_windows_lineending() {
+ let input = b"\r\n";
+ let output = line_ending.parse_peek(Partial::new(&input[..]));
+ assert_parse!(output, Ok((Partial::new(&b""[..]), &b"\r\n"[..])));
+ }
+
+ #[test]
+ fn check_unix_lineending() {
+ let input = b"\n";
+ let output = line_ending.parse_peek(Partial::new(&input[..]));
+ assert_parse!(output, Ok((Partial::new(&b""[..]), &b"\n"[..])));
+ }
+
+ #[test]
+ fn cr_lf() {
+ assert_parse!(
+ crlf.parse_peek(Partial::new(&b"\r\na"[..])),
+ Ok((Partial::new(&b"a"[..]), &b"\r\n"[..]))
+ );
+ assert_parse!(
+ crlf.parse_peek(Partial::new(&b"\r"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_parse!(
+ crlf.parse_peek(Partial::new(&b"\ra"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"\ra"[..]),
+ ErrorKind::Tag
+ )))
+ );
+
+ assert_parse!(
+ crlf.parse_peek(Partial::new("\r\na")),
+ Ok((Partial::new("a"), "\r\n"))
+ );
+ assert_parse!(
+ crlf.parse_peek(Partial::new("\r")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_parse!(
+ crlf.parse_peek(Partial::new("\ra")),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new("\ra"),
+ ErrorKind::Tag
+ )))
+ );
+ }
+
+ #[test]
+ fn end_of_line() {
+ assert_parse!(
+ line_ending.parse_peek(Partial::new(&b"\na"[..])),
+ Ok((Partial::new(&b"a"[..]), &b"\n"[..]))
+ );
+ assert_parse!(
+ line_ending.parse_peek(Partial::new(&b"\r\na"[..])),
+ Ok((Partial::new(&b"a"[..]), &b"\r\n"[..]))
+ );
+ assert_parse!(
+ line_ending.parse_peek(Partial::new(&b"\r"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_parse!(
+ line_ending.parse_peek(Partial::new(&b"\ra"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"\ra"[..]),
+ ErrorKind::Tag
+ )))
+ );
+
+ assert_parse!(
+ line_ending.parse_peek(Partial::new("\na")),
+ Ok((Partial::new("a"), "\n"))
+ );
+ assert_parse!(
+ line_ending.parse_peek(Partial::new("\r\na")),
+ Ok((Partial::new("a"), "\r\n"))
+ );
+ assert_parse!(
+ line_ending.parse_peek(Partial::new("\r")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_parse!(
+ line_ending.parse_peek(Partial::new("\ra")),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new("\ra"),
+ ErrorKind::Tag
+ )))
+ );
+ }
+
+ fn digit_to_i16(input: Partial<&str>) -> IResult<Partial<&str>, i16> {
+ let i = input;
+ let (i, opt_sign) = opt(one_of(['+', '-'])).parse_peek(i)?;
+ let sign = match opt_sign {
+ Some('+') | None => true,
+ Some('-') => false,
+ _ => unreachable!(),
+ };
+
+ let (i, s) = digit1::<_, InputError<_>>.parse_peek(i)?;
+ match s.parse_slice() {
+ Some(n) => {
+ if sign {
+ Ok((i, n))
+ } else {
+ Ok((i, -n))
+ }
+ }
+ None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
+ }
+ }
+
+ fn digit_to_u32(i: Partial<&str>) -> IResult<Partial<&str>, u32> {
+ let (i, s) = digit1.parse_peek(i)?;
+ match s.parse_slice() {
+ Some(n) => Ok((i, n)),
+ None => Err(ErrMode::from_error_kind(&i, ErrorKind::Verify)),
+ }
+ }
+
+ proptest! {
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn ints(s in "\\PC*") {
+ let res1 = digit_to_i16(Partial::new(&s));
+ let res2 = dec_int.parse_peek(Partial::new(s.as_str()));
+ assert_eq!(res1, res2);
+ }
+
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn uints(s in "\\PC*") {
+ let res1 = digit_to_u32(Partial::new(&s));
+ let res2 = dec_uint.parse_peek(Partial::new(s.as_str()));
+ assert_eq!(res1, res2);
+ }
+ }
+
+ #[test]
+ fn hex_uint_tests() {
+ fn hex_u32(input: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+ hex_uint.parse_peek(input)
+ }
+
+ assert_parse!(
+ hex_u32(Partial::new(&b";"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b";"[..]),
+ ErrorKind::Slice
+ )))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"ff;"[..])),
+ Ok((Partial::new(&b";"[..]), 255))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"1be2;"[..])),
+ Ok((Partial::new(&b";"[..]), 7138))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"c5a31be2;"[..])),
+ Ok((Partial::new(&b";"[..]), 3_315_801_058))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"C5A31be2;"[..])),
+ Ok((Partial::new(&b";"[..]), 3_315_801_058))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"00c5a31be2;"[..])), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"00c5a31be2;"[..]),
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"c5a31be201;"[..])), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"c5a31be201;"[..]),
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"ffffffff;"[..])),
+ Ok((Partial::new(&b";"[..]), 4_294_967_295))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"ffffffffffffffff;"[..])), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"ffffffffffffffff;"[..]),
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"ffffffffffffffff"[..])), // overflow
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"ffffffffffffffff"[..]),
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"0x1be2;"[..])),
+ Ok((Partial::new(&b"x1be2;"[..]), 0))
+ );
+ assert_parse!(
+ hex_u32(Partial::new(&b"12af"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+}
diff --git a/src/binary/bits/mod.rs b/src/binary/bits/mod.rs
new file mode 100644
index 0000000..b11ba43
--- /dev/null
+++ b/src/binary/bits/mod.rs
@@ -0,0 +1,365 @@
+//! Bit level parsers
+//!
+
+#[cfg(test)]
+mod tests;
+
+use crate::error::{ErrMode, ErrorConvert, ErrorKind, Needed, ParserError};
+use crate::lib::std::ops::{AddAssign, Div, Shl, Shr};
+use crate::stream::{AsBytes, Stream, StreamIsPartial, ToUsize};
+use crate::trace::trace;
+use crate::{unpeek, IResult, PResult, Parser};
+
+/// Number of bits in a byte
+const BYTE: usize = u8::BITS as usize;
+
+/// Converts a byte-level input to a bit-level input
+///
+/// See [`bytes`] to convert it back.
+///
+/// # Example
+/// ```
+/// use winnow::prelude::*;
+/// use winnow::Bytes;
+/// use winnow::binary::bits::{bits, take};
+/// use winnow::error::InputError;
+///
+/// type Stream<'i> = &'i Bytes;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Bytes::new(b)
+/// }
+///
+/// fn parse(input: Stream<'_>) -> IResult<Stream<'_>, (u8, u8)> {
+/// bits::<_, _, InputError<(_, usize)>, _, _>((take(4usize), take(8usize))).parse_peek(input)
+/// }
+///
+/// let input = stream(&[0x12, 0x34, 0xff, 0xff]);
+///
+/// let output = parse(input).expect("We take 1.5 bytes and the input is longer than 2 bytes");
+///
+/// // The first byte is consumed, the second byte is partially consumed and dropped.
+/// let remaining = output.0;
+/// assert_eq!(remaining, stream(&[0xff, 0xff]));
+///
+/// let parsed = output.1;
+/// assert_eq!(parsed.0, 0x01);
+/// assert_eq!(parsed.1, 0x23);
+/// ```
+pub fn bits<I, O, E1, E2, P>(mut parser: P) -> impl Parser<I, O, E2>
+where
+ E1: ParserError<(I, usize)> + ErrorConvert<E2>,
+ E2: ParserError<I>,
+ I: Stream + Clone,
+ P: Parser<(I, usize), O, E1>,
+{
+ trace(
+ "bits",
+ unpeek(move |input: I| {
+ match parser.parse_peek((input, 0)) {
+ Ok(((rest, offset), result)) => {
+ // If the next byte has been partially read, it will be sliced away as well.
+ // The parser functions might already slice away all fully read bytes.
+ // That's why `offset / BYTE` isn't necessarily needed at all times.
+ let remaining_bytes_index =
+ offset / BYTE + if offset % BYTE == 0 { 0 } else { 1 };
+ let (input, _) = rest.peek_slice(remaining_bytes_index);
+ Ok((input, result))
+ }
+ Err(ErrMode::Incomplete(n)) => {
+ Err(ErrMode::Incomplete(n.map(|u| u.get() / BYTE + 1)))
+ }
+ Err(e) => Err(e.convert()),
+ }
+ }),
+ )
+}
+
+/// Convert a [`bits`] stream back into a byte stream
+///
+/// **Warning:** A partial byte remaining in the input will be ignored and the given parser will
+/// start parsing at the next full byte.
+///
+/// ```
+/// use winnow::prelude::*;
+/// use winnow::Bytes;
+/// use winnow::binary::bits::{bits, bytes, take};
+/// use winnow::combinator::rest;
+/// use winnow::error::InputError;
+///
+/// type Stream<'i> = &'i Bytes;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Bytes::new(b)
+/// }
+///
+/// fn parse(input: Stream<'_>) -> IResult<Stream<'_>, (u8, u8, &[u8])> {
+/// bits::<_, _, InputError<(_, usize)>, _, _>((
+/// take(4usize),
+/// take(8usize),
+/// bytes::<_, _, InputError<_>, _, _>(rest)
+/// )).parse_peek(input)
+/// }
+///
+/// let input = stream(&[0x12, 0x34, 0xff, 0xff]);
+///
+/// assert_eq!(parse(input), Ok(( stream(&[]), (0x01, 0x23, &[0xff, 0xff][..]) )));
+/// ```
+pub fn bytes<I, O, E1, E2, P>(mut parser: P) -> impl Parser<(I, usize), O, E2>
+where
+ E1: ParserError<I> + ErrorConvert<E2>,
+ E2: ParserError<(I, usize)>,
+ I: Stream<Token = u8> + Clone,
+ P: Parser<I, O, E1>,
+{
+ trace(
+ "bytes",
+ unpeek(move |(input, offset): (I, usize)| {
+ let (inner, _) = if offset % BYTE != 0 {
+ input.peek_slice(1 + offset / BYTE)
+ } else {
+ input.peek_slice(offset / BYTE)
+ };
+ let i = (input, offset);
+ match parser.parse_peek(inner) {
+ Ok((rest, res)) => Ok(((rest, 0), res)),
+ Err(ErrMode::Incomplete(Needed::Unknown)) => {
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ }
+ Err(ErrMode::Incomplete(Needed::Size(sz))) => {
+ Err(match sz.get().checked_mul(BYTE) {
+ Some(v) => ErrMode::Incomplete(Needed::new(v)),
+ None => ErrMode::Cut(E2::assert(
+ &i,
+ "overflow in turning needed bytes into needed bits",
+ )),
+ })
+ }
+ Err(e) => Err(e.convert()),
+ }
+ }),
+ )
+}
+
+/// Parse taking `count` bits
+///
+/// # Example
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::Bytes;
+/// # use winnow::error::{InputError, ErrorKind};
+/// use winnow::binary::bits::take;
+///
+/// type Stream<'i> = &'i Bytes;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Bytes::new(b)
+/// }
+///
+/// fn parser(input: (Stream<'_>, usize), count: usize)-> IResult<(Stream<'_>, usize), u8> {
+/// take(count).parse_peek(input)
+/// }
+///
+/// // Consumes 0 bits, returns 0
+/// assert_eq!(parser((stream(&[0b00010010]), 0), 0), Ok(((stream(&[0b00010010]), 0), 0)));
+///
+/// // Consumes 4 bits, returns their values and increase offset to 4
+/// assert_eq!(parser((stream(&[0b00010010]), 0), 4), Ok(((stream(&[0b00010010]), 4), 0b00000001)));
+///
+/// // Consumes 4 bits, offset is 4, returns their values and increase offset to 0 of next byte
+/// assert_eq!(parser((stream(&[0b00010010]), 4), 4), Ok(((stream(&[]), 0), 0b00000010)));
+///
+/// // Tries to consume 12 bits but only 8 are available
+/// assert_eq!(parser((stream(&[0b00010010]), 0), 12), Err(winnow::error::ErrMode::Backtrack(InputError::new((stream(&[0b00010010]), 0), ErrorKind::Eof))));
+/// ```
+#[inline(always)]
+pub fn take<I, O, C, E: ParserError<(I, usize)>>(count: C) -> impl Parser<(I, usize), O, E>
+where
+ I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone,
+ C: ToUsize,
+ O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>,
+{
+ let count = count.to_usize();
+ trace(
+ "take",
+ unpeek(move |input: (I, usize)| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_::<_, _, _, true>(input, count)
+ } else {
+ take_::<_, _, _, false>(input, count)
+ }
+ }),
+ )
+}
+
+fn take_<I, O, E: ParserError<(I, usize)>, const PARTIAL: bool>(
+ (input, bit_offset): (I, usize),
+ count: usize,
+) -> IResult<(I, usize), O, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8> + AsBytes + Clone,
+ O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>,
+{
+ if count == 0 {
+ Ok(((input, bit_offset), 0u8.into()))
+ } else {
+ if input.eof_offset() * BYTE < count + bit_offset {
+ if PARTIAL && input.is_partial() {
+ Err(ErrMode::Incomplete(Needed::new(count)))
+ } else {
+ Err(ErrMode::from_error_kind(
+ &(input, bit_offset),
+ ErrorKind::Eof,
+ ))
+ }
+ } else {
+ let cnt = (count + bit_offset).div(BYTE);
+ let mut acc: O = 0_u8.into();
+ let mut offset: usize = bit_offset;
+ let mut remaining: usize = count;
+ let mut end_offset: usize = 0;
+
+ for byte in input.as_bytes().iter().copied().take(cnt + 1) {
+ if remaining == 0 {
+ break;
+ }
+ let val: O = if offset == 0 {
+ byte.into()
+ } else {
+ (byte << offset >> offset).into()
+ };
+
+ if remaining < BYTE - offset {
+ acc += val >> (BYTE - offset - remaining);
+ end_offset = remaining + offset;
+ break;
+ } else {
+ acc += val << (remaining - (BYTE - offset));
+ remaining -= BYTE - offset;
+ offset = 0;
+ }
+ }
+ let (input, _) = input.peek_slice(cnt);
+ Ok(((input, end_offset), acc))
+ }
+ }
+}
+
+/// Parse taking `count` bits and comparing them to `pattern`
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::Bytes;
+/// # use winnow::error::{InputError, ErrorKind};
+/// use winnow::binary::bits::tag;
+///
+/// type Stream<'i> = &'i Bytes;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Bytes::new(b)
+/// }
+///
+/// /// Compare the lowest `count` bits of `input` against the lowest `count` bits of `pattern`.
+/// /// Return Ok and the matching section of `input` if there's a match.
+/// /// Return Err if there's no match.
+/// fn parser(pattern: u8, count: u8, input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), u8> {
+/// tag(pattern, count).parse_peek(input)
+/// }
+///
+/// // The lowest 4 bits of 0b00001111 match the lowest 4 bits of 0b11111111.
+/// assert_eq!(
+/// parser(0b0000_1111, 4, (stream(&[0b1111_1111]), 0)),
+/// Ok(((stream(&[0b1111_1111]), 4), 0b0000_1111))
+/// );
+///
+/// // The lowest bit of 0b00001111 matches the lowest bit of 0b11111111 (both are 1).
+/// assert_eq!(
+/// parser(0b00000001, 1, (stream(&[0b11111111]), 0)),
+/// Ok(((stream(&[0b11111111]), 1), 0b00000001))
+/// );
+///
+/// // The lowest 2 bits of 0b11111111 and 0b00000001 are different.
+/// assert_eq!(
+/// parser(0b000000_01, 2, (stream(&[0b111111_11]), 0)),
+/// Err(winnow::error::ErrMode::Backtrack(InputError::new(
+/// (stream(&[0b11111111]), 0),
+/// ErrorKind::Tag
+/// )))
+/// );
+///
+/// // The lowest 8 bits of 0b11111111 and 0b11111110 are different.
+/// assert_eq!(
+/// parser(0b11111110, 8, (stream(&[0b11111111]), 0)),
+/// Err(winnow::error::ErrMode::Backtrack(InputError::new(
+/// (stream(&[0b11111111]), 0),
+/// ErrorKind::Tag
+/// )))
+/// );
+/// ```
+#[inline(always)]
+#[doc(alias = "literal")]
+#[doc(alias = "just")]
+pub fn tag<I, O, C, E: ParserError<(I, usize)>>(
+ pattern: O,
+ count: C,
+) -> impl Parser<(I, usize), O, E>
+where
+ I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone,
+ C: ToUsize,
+ O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O> + PartialEq,
+{
+ let count = count.to_usize();
+ trace("tag", move |input: &mut (I, usize)| {
+ let start = input.checkpoint();
+
+ take(count).parse_next(input).and_then(|o| {
+ if pattern == o {
+ Ok(o)
+ } else {
+ input.reset(start);
+ Err(ErrMode::Backtrack(E::from_error_kind(
+ input,
+ ErrorKind::Tag,
+ )))
+ }
+ })
+ })
+}
+
+/// Parses one specific bit as a bool.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::Bytes;
+/// # use winnow::error::{InputError, ErrorKind};
+/// use winnow::binary::bits::bool;
+///
+/// type Stream<'i> = &'i Bytes;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Bytes::new(b)
+/// }
+///
+/// fn parse(input: (Stream<'_>, usize)) -> IResult<(Stream<'_>, usize), bool> {
+/// bool.parse_peek(input)
+/// }
+///
+/// assert_eq!(parse((stream(&[0b10000000]), 0)), Ok(((stream(&[0b10000000]), 1), true)));
+/// assert_eq!(parse((stream(&[0b10000000]), 1)), Ok(((stream(&[0b10000000]), 2), false)));
+/// ```
+#[doc(alias = "any")]
+pub fn bool<I, E: ParserError<(I, usize)>>(input: &mut (I, usize)) -> PResult<bool, E>
+where
+ I: Stream<Token = u8> + AsBytes + StreamIsPartial + Clone,
+{
+ trace("bool", |input: &mut (I, usize)| {
+ let bit: u32 = take(1usize).parse_next(input)?;
+ Ok(bit != 0)
+ })
+ .parse_next(input)
+}
diff --git a/src/binary/bits/tests.rs b/src/binary/bits/tests.rs
new file mode 100644
index 0000000..41207c6
--- /dev/null
+++ b/src/binary/bits/tests.rs
@@ -0,0 +1,191 @@
+use super::*;
+use crate::error::InputError;
+use crate::Partial;
+
+#[test]
+/// Take the `bits` function and assert that remaining bytes are correctly returned, if the
+/// previous bytes are fully consumed
+fn test_complete_byte_consumption_bits() {
+ let input = &[0x12, 0x34, 0x56, 0x78][..];
+
+ // Take 3 bit slices with sizes [4, 8, 4].
+ let result: IResult<&[u8], (u8, u8, u8)> =
+ bits::<_, _, InputError<(&[u8], usize)>, _, _>((take(4usize), take(8usize), take(4usize)))
+ .parse_peek(input);
+
+ let output = result.expect("We take 2 bytes and the input is longer than 2 bytes");
+
+ let remaining = output.0;
+ assert_eq!(remaining, [0x56, 0x78]);
+
+ let parsed = output.1;
+ assert_eq!(parsed.0, 0x01);
+ assert_eq!(parsed.1, 0x23);
+ assert_eq!(parsed.2, 0x04);
+}
+
+#[test]
+/// Take the `bits` function and assert that remaining bytes are correctly returned, if the
+/// previous bytes are NOT fully consumed. Partially consumed bytes are supposed to be dropped.
+/// I.e. if we consume 1.5 bytes of 4 bytes, 2 bytes will be returned, bits 13-16 will be
+/// dropped.
+fn test_partial_byte_consumption_bits() {
+ let input = &[0x12, 0x34, 0x56, 0x78][..];
+
+ // Take bit slices with sizes [4, 8].
+ let result: IResult<&[u8], (u8, u8)> =
+ bits::<_, _, InputError<(&[u8], usize)>, _, _>((take(4usize), take(8usize)))
+ .parse_peek(input);
+
+ let output = result.expect("We take 1.5 bytes and the input is longer than 2 bytes");
+
+ let remaining = output.0;
+ assert_eq!(remaining, [0x56, 0x78]);
+
+ let parsed = output.1;
+ assert_eq!(parsed.0, 0x01);
+ assert_eq!(parsed.1, 0x23);
+}
+
+#[test]
+#[cfg(feature = "std")]
+/// Ensure that in Incomplete error is thrown, if too few bytes are passed for a given parser.
+fn test_incomplete_bits() {
+ let input = Partial::new(&[0x12][..]);
+
+ // Take bit slices with sizes [4, 8].
+ let result: IResult<_, (u8, u8)> =
+ bits::<_, _, InputError<(_, usize)>, _, _>((take(4usize), take(8usize))).parse_peek(input);
+
+ assert!(result.is_err());
+ let error = result.err().unwrap();
+ assert_eq!("Parsing requires 2 bytes/chars", error.to_string());
+}
+
+#[test]
+fn test_take_complete_0() {
+ let input = &[0b00010010][..];
+ let count = 0usize;
+ assert_eq!(count, 0usize);
+ let offset = 0usize;
+
+ let result: crate::IResult<(&[u8], usize), usize> = take(count).parse_peek((input, offset));
+
+ assert_eq!(result, Ok(((input, offset), 0)));
+}
+
+#[test]
+fn test_take_complete_eof() {
+ let input = &[0b00010010][..];
+
+ let result: crate::IResult<(&[u8], usize), usize> = take(1usize).parse_peek((input, 8));
+
+ assert_eq!(
+ result,
+ Err(crate::error::ErrMode::Backtrack(InputError::new(
+ (input, 8),
+ ErrorKind::Eof
+ )))
+ );
+}
+
+#[test]
+fn test_take_complete_span_over_multiple_bytes() {
+ let input = &[0b00010010, 0b00110100, 0b11111111, 0b11111111][..];
+
+ let result: crate::IResult<(&[u8], usize), usize> = take(24usize).parse_peek((input, 4));
+
+ assert_eq!(
+ result,
+ Ok((([0b11111111].as_ref(), 4), 0b1000110100111111111111))
+ );
+}
+
+#[test]
+fn test_take_partial_0() {
+ let input = Partial::new(&[][..]);
+ let count = 0usize;
+ assert_eq!(count, 0usize);
+ let offset = 0usize;
+
+ let result: crate::IResult<(_, usize), usize> = take(count).parse_peek((input, offset));
+
+ assert_eq!(result, Ok(((input, offset), 0)));
+}
+
+#[test]
+fn test_tag_partial_ok() {
+ let input = Partial::new(&[0b00011111][..]);
+ let offset = 0usize;
+ let bits_to_take = 4usize;
+ let value_to_tag = 0b0001;
+
+ let result: crate::IResult<(_, usize), usize> =
+ tag(value_to_tag, bits_to_take).parse_peek((input, offset));
+
+ assert_eq!(result, Ok(((input, bits_to_take), value_to_tag)));
+}
+
+#[test]
+fn test_tag_partial_err() {
+ let input = Partial::new(&[0b00011111][..]);
+ let offset = 0usize;
+ let bits_to_take = 4usize;
+ let value_to_tag = 0b1111;
+
+ let result: crate::IResult<(_, usize), usize> =
+ tag(value_to_tag, bits_to_take).parse_peek((input, offset));
+
+ assert_eq!(
+ result,
+ Err(crate::error::ErrMode::Backtrack(InputError::new(
+ (input, offset),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn test_bool_0_complete() {
+ let input = [0b10000000].as_ref();
+
+ let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 0));
+
+ assert_eq!(result, Ok(((input, 1), true)));
+}
+
+#[test]
+fn test_bool_eof_complete() {
+ let input = [0b10000000].as_ref();
+
+ let result: crate::IResult<(&[u8], usize), bool> = bool.parse_peek((input, 8));
+
+ assert_eq!(
+ result,
+ Err(crate::error::ErrMode::Backtrack(InputError::new(
+ (input, 8),
+ ErrorKind::Eof
+ )))
+ );
+}
+
+#[test]
+fn test_bool_0_partial() {
+ let input = Partial::new([0b10000000].as_ref());
+
+ let result: crate::IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 0));
+
+ assert_eq!(result, Ok(((input, 1), true)));
+}
+
+#[test]
+fn test_bool_eof_partial() {
+ let input = Partial::new([0b10000000].as_ref());
+
+ let result: crate::IResult<(Partial<&[u8]>, usize), bool> = bool.parse_peek((input, 8));
+
+ assert_eq!(
+ result,
+ Err(crate::error::ErrMode::Incomplete(Needed::new(1)))
+ );
+}
diff --git a/src/binary/mod.rs b/src/binary/mod.rs
new file mode 100644
index 0000000..8b2ee74
--- /dev/null
+++ b/src/binary/mod.rs
@@ -0,0 +1,2550 @@
+//! Parsers recognizing numbers
+
+#![allow(clippy::match_same_arms)]
+
+pub mod bits;
+
+#[cfg(test)]
+mod tests;
+
+use crate::combinator::repeat;
+use crate::error::ErrMode;
+use crate::error::ErrorKind;
+use crate::error::Needed;
+use crate::error::ParserError;
+use crate::lib::std::ops::{Add, Shl};
+use crate::stream::Accumulate;
+use crate::stream::{AsBytes, Stream, StreamIsPartial};
+use crate::stream::{ToUsize, UpdateSlice};
+use crate::token::take;
+use crate::trace::trace;
+use crate::PResult;
+use crate::Parser;
+
+/// Configurable endianness
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Endianness {
+ /// Big endian
+ Big,
+ /// Little endian
+ Little,
+ /// Will match the host's endianness
+ Native,
+}
+
+/// Recognizes an unsigned 1 byte integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_u8;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u8> {
+/// be_u8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00)));
+/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_u8;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+/// be_u8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn be_u8<I, E: ParserError<I>>(input: &mut I) -> PResult<u8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ u8(input)
+}
+
+/// Recognizes a big endian unsigned 2 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_u16;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u16> {
+/// be_u16.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_u16;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> {
+/// be_u16.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn be_u16<I, E: ParserError<I>>(input: &mut I) -> PResult<u16, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_u16", move |input: &mut I| be_uint(input, 2)).parse_next(input)
+}
+
+/// Recognizes a big endian unsigned 3 byte integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_u24;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u32> {
+/// be_u24.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_u24;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+/// be_u24.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x000102)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn be_u24<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_u23", move |input: &mut I| be_uint(input, 3)).parse_next(input)
+}
+
+/// Recognizes a big endian unsigned 4 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_u32;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u32> {
+/// be_u32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_u32;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+/// be_u32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn be_u32<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_u32", move |input: &mut I| be_uint(input, 4)).parse_next(input)
+}
+
+/// Recognizes a big endian unsigned 8 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_u64;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u64> {
+/// be_u64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_u64;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> {
+/// be_u64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001020304050607)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn be_u64<I, E: ParserError<I>>(input: &mut I) -> PResult<u64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_u64", move |input: &mut I| be_uint(input, 8)).parse_next(input)
+}
+
+/// Recognizes a big endian unsigned 16 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_u128;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u128> {
+/// be_u128.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_u128;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u128> {
+/// be_u128.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203040506070809101112131415)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+/// ```
+#[inline(always)]
+pub fn be_u128<I, E: ParserError<I>>(input: &mut I) -> PResult<u128, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_u128", move |input: &mut I| be_uint(input, 16)).parse_next(input)
+}
+
+#[inline]
+fn be_uint<I, Uint, E: ParserError<I>>(input: &mut I, bound: usize) -> PResult<Uint, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+ Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+{
+ debug_assert_ne!(bound, 1, "to_be_uint needs extra work to avoid overflow");
+ take(bound)
+ .map(|n: <I as Stream>::Slice| to_be_uint(n.as_bytes()))
+ .parse_next(input)
+}
+
+#[inline]
+fn to_be_uint<Uint>(number: &[u8]) -> Uint
+where
+ Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+{
+ let mut res = Uint::default();
+ for byte in number.iter().copied() {
+ res = (res << 8) + byte.into();
+ }
+
+ res
+}
+
+/// Recognizes a signed 1 byte integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_i8;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i8> {
+/// be_i8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00)));
+/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_i8;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i8> {
+/// be_i8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn be_i8<I, E: ParserError<I>>(input: &mut I) -> PResult<i8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ i8(input)
+}
+
+/// Recognizes a big endian signed 2 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_i16;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i16> {
+/// be_i16.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_i16;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> {
+/// be_i16.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn be_i16<I, E: ParserError<I>>(input: &mut I) -> PResult<i16, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_i16", move |input: &mut I| {
+ be_uint::<_, u16, _>(input, 2).map(|n| n as i16)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a big endian signed 3 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_i24;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i32> {
+/// be_i24.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_i24;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> {
+/// be_i24.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x000102)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn be_i24<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_i24", move |input: &mut I| {
+ be_uint::<_, u32, _>(input, 3).map(|n| {
+ // Same as the unsigned version but we need to sign-extend manually here
+ let n = if n & 0x80_00_00 != 0 {
+ (n | 0xff_00_00_00) as i32
+ } else {
+ n as i32
+ };
+ n
+ })
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a big endian signed 4 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_i32;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i32> {
+/// be_i32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_i32;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> {
+/// be_i32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(4))));
+/// ```
+#[inline(always)]
+pub fn be_i32<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_i32", move |input: &mut I| {
+ be_uint::<_, u32, _>(input, 4).map(|n| n as i32)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a big endian signed 8 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_i64;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i64> {
+/// be_i64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_i64;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> {
+/// be_i64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0001020304050607)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn be_i64<I, E: ParserError<I>>(input: &mut I) -> PResult<i64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_i64", move |input: &mut I| {
+ be_uint::<_, u64, _>(input, 8).map(|n| n as i64)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a big endian signed 16 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_i128;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i128> {
+/// be_i128.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_i128;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i128> {
+/// be_i128.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x00010203040506070809101112131415)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+/// ```
+#[inline(always)]
+pub fn be_i128<I, E: ParserError<I>>(input: &mut I) -> PResult<i128, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_i128", move |input: &mut I| {
+ be_uint::<_, u128, _>(input, 16).map(|n| n as i128)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes an unsigned 1 byte integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_u8;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u8> {
+/// le_u8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00)));
+/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_u8;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+/// le_u8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn le_u8<I, E: ParserError<I>>(input: &mut I) -> PResult<u8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ u8(input)
+}
+
+/// Recognizes a little endian unsigned 2 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_u16;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u16> {
+/// le_u16.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_u16;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> {
+/// le_u16::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn le_u16<I, E: ParserError<I>>(input: &mut I) -> PResult<u16, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_u16", move |input: &mut I| le_uint(input, 2)).parse_next(input)
+}
+
+/// Recognizes a little endian unsigned 3 byte integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_u24;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u32> {
+/// le_u24.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_u24;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+/// le_u24::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn le_u24<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_u24", move |input: &mut I| le_uint(input, 3)).parse_next(input)
+}
+
+/// Recognizes a little endian unsigned 4 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_u32;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u32> {
+/// le_u32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_u32;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+/// le_u32::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x03020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn le_u32<I, E: ParserError<I>>(input: &mut I) -> PResult<u32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_u32", move |input: &mut I| le_uint(input, 4)).parse_next(input)
+}
+
+/// Recognizes a little endian unsigned 8 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_u64;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u64> {
+/// le_u64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_u64;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> {
+/// le_u64::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0706050403020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn le_u64<I, E: ParserError<I>>(input: &mut I) -> PResult<u64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_u64", move |input: &mut I| le_uint(input, 8)).parse_next(input)
+}
+
+/// Recognizes a little endian unsigned 16 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_u128;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u128> {
+/// le_u128.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_u128;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u128> {
+/// le_u128::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x15141312111009080706050403020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+/// ```
+#[inline(always)]
+pub fn le_u128<I, E: ParserError<I>>(input: &mut I) -> PResult<u128, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_u128", move |input: &mut I| le_uint(input, 16)).parse_next(input)
+}
+
+#[inline]
+fn le_uint<I, Uint, E: ParserError<I>>(input: &mut I, bound: usize) -> PResult<Uint, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+ Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+{
+ take(bound)
+ .map(|n: <I as Stream>::Slice| to_le_uint(n.as_bytes()))
+ .parse_next(input)
+}
+
+#[inline]
+fn to_le_uint<Uint>(number: &[u8]) -> Uint
+where
+ Uint: Default + Shl<u8, Output = Uint> + Add<Uint, Output = Uint> + From<u8>,
+{
+ let mut res = Uint::default();
+ for (index, byte) in number.iter_offsets() {
+ res = res + (Uint::from(byte) << (8 * index as u8));
+ }
+
+ res
+}
+
+/// Recognizes a signed 1 byte integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_i8;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i8> {
+/// le_i8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00)));
+/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_i8;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i8> {
+/// le_i8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"\x01abcd"[..]), 0x00)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn le_i8<I, E: ParserError<I>>(input: &mut I) -> PResult<i8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ i8(input)
+}
+
+/// Recognizes a little endian signed 2 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_i16;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i16> {
+/// le_i16.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_i16;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> {
+/// le_i16::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn le_i16<I, E: ParserError<I>>(input: &mut I) -> PResult<i16, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_i16", move |input: &mut I| {
+ le_uint::<_, u16, _>(input, 2).map(|n| n as i16)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a little endian signed 3 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_i24;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i32> {
+/// le_i24.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_i24;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> {
+/// le_i24::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn le_i24<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_i24", move |input: &mut I| {
+ le_uint::<_, u32, _>(input, 3).map(|n| {
+ // Same as the unsigned version but we need to sign-extend manually here
+ let n = if n & 0x80_00_00 != 0 {
+ (n | 0xff_00_00_00) as i32
+ } else {
+ n as i32
+ };
+ n
+ })
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a little endian signed 4 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_i32;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i32> {
+/// le_i32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_i32;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> {
+/// le_i32::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x03020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn le_i32<I, E: ParserError<I>>(input: &mut I) -> PResult<i32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_i32", move |input: &mut I| {
+ le_uint::<_, u32, _>(input, 4).map(|n| n as i32)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a little endian signed 8 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_i64;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i64> {
+/// le_i64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_i64;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> {
+/// le_i64::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x0706050403020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn le_i64<I, E: ParserError<I>>(input: &mut I) -> PResult<i64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_i64", move |input: &mut I| {
+ le_uint::<_, u64, _>(input, 8).map(|n| n as i64)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a little endian signed 16 bytes integer.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_i128;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i128> {
+/// le_i128.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100)));
+/// assert_eq!(parser(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_i128;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i128> {
+/// le_i128::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15abcd"[..])), Ok((Partial::new(&b"abcd"[..]), 0x15141312111009080706050403020100)));
+/// assert_eq!(parser(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+/// ```
+#[inline(always)]
+pub fn le_i128<I, E: ParserError<I>>(input: &mut I) -> PResult<i128, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_i128", move |input: &mut I| {
+ le_uint::<_, u128, _>(input, 16).map(|n| n as i128)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes an unsigned 1 byte integer
+///
+/// **Note:** that endianness does not apply to 1 byte numbers.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::u8;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], u8> {
+/// u8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00)));
+/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::u8;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+/// u8::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"\x03abcefg"[..]), 0x00)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn u8<I, E: ParserError<I>>(input: &mut I) -> PResult<u8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ trace("u8", move |input: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ u8_::<_, _, true>(input)
+ } else {
+ u8_::<_, _, false>(input)
+ }
+ })
+ .parse_next(input)
+}
+
+fn u8_<I, E: ParserError<I>, const PARTIAL: bool>(input: &mut I) -> PResult<u8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ input.next_token().ok_or_else(|| {
+ if PARTIAL && input.is_partial() {
+ ErrMode::Incomplete(Needed::new(1))
+ } else {
+ ErrMode::Backtrack(E::from_error_kind(input, ErrorKind::Token))
+ }
+ })
+}
+
+/// Recognizes an unsigned 2 bytes integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u16 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u16 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::u16;
+///
+/// let be_u16 = |s| {
+/// u16(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003)));
+/// assert_eq!(be_u16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_u16 = |s| {
+/// u16(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300)));
+/// assert_eq!(le_u16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::u16;
+///
+/// let be_u16 = |s| {
+/// u16::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0003)));
+/// assert_eq!(be_u16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+///
+/// let le_u16 = |s| {
+/// u16::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0300)));
+/// assert_eq!(le_u16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn u16<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u16, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_u16,
+ Endianness::Little => le_u16,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_u16,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_u16,
+ }
+ }(input)
+}
+
+/// Recognizes an unsigned 3 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u24 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u24 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::u24;
+///
+/// let be_u24 = |s| {
+/// u24(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305)));
+/// assert_eq!(be_u24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_u24 = |s| {
+/// u24(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300)));
+/// assert_eq!(le_u24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::u24;
+///
+/// let be_u24 = |s| {
+/// u24::<_,InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x000305)));
+/// assert_eq!(be_u24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+///
+/// let le_u24 = |s| {
+/// u24::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x050300)));
+/// assert_eq!(le_u24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn u24<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_u24,
+ Endianness::Little => le_u24,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_u24,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_u24,
+ }
+ }(input)
+}
+
+/// Recognizes an unsigned 4 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u32 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u32 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::u32;
+///
+/// let be_u32 = |s| {
+/// u32(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507)));
+/// assert_eq!(be_u32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_u32 = |s| {
+/// u32(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300)));
+/// assert_eq!(le_u32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::u32;
+///
+/// let be_u32 = |s| {
+/// u32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00030507)));
+/// assert_eq!(be_u32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+///
+/// let le_u32 = |s| {
+/// u32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07050300)));
+/// assert_eq!(le_u32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn u32<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_u32,
+ Endianness::Little => le_u32,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_u32,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_u32,
+ }
+ }(input)
+}
+
+/// Recognizes an unsigned 8 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u64 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u64 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::u64;
+///
+/// let be_u64 = |s| {
+/// u64(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607)));
+/// assert_eq!(be_u64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_u64 = |s| {
+/// u64(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100)));
+/// assert_eq!(le_u64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::u64;
+///
+/// let be_u64 = |s| {
+/// u64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0001020304050607)));
+/// assert_eq!(be_u64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+///
+/// let le_u64 = |s| {
+/// u64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0706050403020100)));
+/// assert_eq!(le_u64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn u64<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_u64,
+ Endianness::Little => le_u64,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_u64,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_u64,
+ }
+ }(input)
+}
+
+/// Recognizes an unsigned 16 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian u128 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian u128 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::u128;
+///
+/// let be_u128 = |s| {
+/// u128(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607)));
+/// assert_eq!(be_u128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_u128 = |s| {
+/// u128(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100)));
+/// assert_eq!(le_u128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::u128;
+///
+/// let be_u128 = |s| {
+/// u128::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_u128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00010203040506070001020304050607)));
+/// assert_eq!(be_u128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+///
+/// let le_u128 = |s| {
+/// u128::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_u128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07060504030201000706050403020100)));
+/// assert_eq!(le_u128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+/// ```
+#[inline(always)]
+pub fn u128<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, u128, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_u128,
+ Endianness::Little => le_u128,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_u128,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_u128,
+ }
+ }(input)
+}
+
+/// Recognizes a signed 1 byte integer
+///
+/// **Note:** that endianness does not apply to 1 byte numbers.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::i8;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], i8> {
+/// i8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&b"\x00\x03abcefg"[..]), Ok((&b"\x03abcefg"[..], 0x00)));
+/// assert_eq!(parser(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&[][..], ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::i8;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i8> {
+/// i8.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"\x03abcefg"[..]), 0x00)));
+/// assert_eq!(parser(Partial::new(&b""[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn i8<I, E: ParserError<I>>(input: &mut I) -> PResult<i8, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+{
+ trace("i8", move |input: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ u8_::<_, _, true>(input)
+ } else {
+ u8_::<_, _, false>(input)
+ }
+ .map(|n| n as i8)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a signed 2 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i16 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i16 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::i16;
+///
+/// let be_i16 = |s| {
+/// i16(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0003)));
+/// assert_eq!(be_i16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_i16 = |s| {
+/// i16(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i16(&b"\x00\x03abcefg"[..]), Ok((&b"abcefg"[..], 0x0300)));
+/// assert_eq!(le_i16(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::i16;
+///
+/// let be_i16 = |s| {
+/// i16::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0003)));
+/// assert_eq!(be_i16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+///
+/// let le_i16 = |s| {
+/// i16::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i16(Partial::new(&b"\x00\x03abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0300)));
+/// assert_eq!(le_i16(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn i16<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i16, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_i16,
+ Endianness::Little => le_i16,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_i16,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_i16,
+ }
+ }(input)
+}
+
+/// Recognizes a signed 3 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i24 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i24 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::i24;
+///
+/// let be_i24 = |s| {
+/// i24(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x000305)));
+/// assert_eq!(be_i24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_i24 = |s| {
+/// i24(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i24(&b"\x00\x03\x05abcefg"[..]), Ok((&b"abcefg"[..], 0x050300)));
+/// assert_eq!(le_i24(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::i24;
+///
+/// let be_i24 = |s| {
+/// i24::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x000305)));
+/// assert_eq!(be_i24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+///
+/// let le_i24 = |s| {
+/// i24::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i24(Partial::new(&b"\x00\x03\x05abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x050300)));
+/// assert_eq!(le_i24(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+#[inline(always)]
+pub fn i24<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_i24,
+ Endianness::Little => le_i24,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_i24,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_i24,
+ }
+ }(input)
+}
+
+/// Recognizes a signed 4 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i32 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i32 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::i32;
+///
+/// let be_i32 = |s| {
+/// i32(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00030507)));
+/// assert_eq!(be_i32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_i32 = |s| {
+/// i32(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i32(&b"\x00\x03\x05\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07050300)));
+/// assert_eq!(le_i32(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::i32;
+///
+/// let be_i32 = |s| {
+/// i32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00030507)));
+/// assert_eq!(be_i32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+///
+/// let le_i32 = |s| {
+/// i32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i32(Partial::new(&b"\x00\x03\x05\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07050300)));
+/// assert_eq!(le_i32(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn i32<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_i32,
+ Endianness::Little => le_i32,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_i32,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_i32,
+ }
+ }(input)
+}
+
+/// Recognizes a signed 8 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i64 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i64 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::i64;
+///
+/// let be_i64 = |s| {
+/// i64(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0001020304050607)));
+/// assert_eq!(be_i64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_i64 = |s| {
+/// i64(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i64(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x0706050403020100)));
+/// assert_eq!(le_i64(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::i64;
+///
+/// let be_i64 = |s| {
+/// i64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0001020304050607)));
+/// assert_eq!(be_i64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+///
+/// let le_i64 = |s| {
+/// i64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i64(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x0706050403020100)));
+/// assert_eq!(le_i64(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn i64<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_i64,
+ Endianness::Little => le_i64,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_i64,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_i64,
+ }
+ }(input)
+}
+
+/// Recognizes a signed 16 byte integer
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian i128 integer,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian i128 integer.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::i128;
+///
+/// let be_i128 = |s| {
+/// i128(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x00010203040506070001020304050607)));
+/// assert_eq!(be_i128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+///
+/// let le_i128 = |s| {
+/// i128(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i128(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..]), Ok((&b"abcefg"[..], 0x07060504030201000706050403020100)));
+/// assert_eq!(le_i128(&b"\x01"[..]), Err(ErrMode::Backtrack(InputError::new(&[0x01][..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::i128;
+///
+/// let be_i128 = |s| {
+/// i128::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_i128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x00010203040506070001020304050607)));
+/// assert_eq!(be_i128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+///
+/// let le_i128 = |s| {
+/// i128::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_i128(Partial::new(&b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07abcefg"[..])), Ok((Partial::new(&b"abcefg"[..]), 0x07060504030201000706050403020100)));
+/// assert_eq!(le_i128(Partial::new(&b"\x01"[..])), Err(ErrMode::Incomplete(Needed::new(15))));
+/// ```
+#[inline(always)]
+pub fn i128<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, i128, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_i128,
+ Endianness::Little => le_i128,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_i128,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_i128,
+ }
+ }(input)
+}
+
+/// Recognizes a big endian 4 bytes floating point number.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_f32;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], f32> {
+/// be_f32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&[0x41, 0x48, 0x00, 0x00][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_f32;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f32> {
+/// be_f32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&[0x40, 0x29, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 2.640625)));
+/// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn be_f32<I, E: ParserError<I>>(input: &mut I) -> PResult<f32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_f32", move |input: &mut I| {
+ be_uint::<_, u32, _>(input, 4).map(f32::from_bits)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a big endian 8 bytes floating point number.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::be_f64;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], f64> {
+/// be_f64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::be_f64;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f64> {
+/// be_f64::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5)));
+/// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn be_f64<I, E: ParserError<I>>(input: &mut I) -> PResult<f64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_f64", move |input: &mut I| {
+ be_uint::<_, u64, _>(input, 8).map(f64::from_bits)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a little endian 4 bytes floating point number.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_f32;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], f32> {
+/// le_f32.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&[0x00, 0x00, 0x48, 0x41][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_f32;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f32> {
+/// le_f32::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&[0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 12.5)));
+/// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(3))));
+/// ```
+#[inline(always)]
+pub fn le_f32<I, E: ParserError<I>>(input: &mut I) -> PResult<f32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("le_f32", move |input: &mut I| {
+ le_uint::<_, u32, _>(input, 4).map(f32::from_bits)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a little endian 8 bytes floating point number.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::le_f64;
+///
+/// fn parser(s: &[u8]) -> IResult<&[u8], f64> {
+/// le_f64.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(parser(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::binary::le_f64;
+///
+/// fn parser(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, f64> {
+/// le_f64::<_, InputError<_>>.parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 3145728.0)));
+/// assert_eq!(parser(Partial::new(&[0x01][..])), Err(ErrMode::Incomplete(Needed::new(7))));
+/// ```
+#[inline(always)]
+pub fn le_f64<I, E: ParserError<I>>(input: &mut I) -> PResult<f64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ trace("be_f64", move |input: &mut I| {
+ le_uint::<_, u64, _>(input, 8).map(f64::from_bits)
+ })
+ .parse_next(input)
+}
+
+/// Recognizes a 4 byte floating point number
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian f32 float,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian f32 float.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::f32;
+///
+/// let be_f32 = |s| {
+/// f32(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_f32(&[0x41, 0x48, 0x00, 0x00][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(be_f32(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+///
+/// let le_f32 = |s| {
+/// f32(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_f32(&[0x00, 0x00, 0x48, 0x41][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(le_f32(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::f32;
+///
+/// let be_f32 = |s| {
+/// f32::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_f32(Partial::new(&[0x41, 0x48, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5)));
+/// assert_eq!(be_f32(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+///
+/// let le_f32 = |s| {
+/// f32::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_f32(Partial::new(&[0x00, 0x00, 0x48, 0x41][..])), Ok((Partial::new(&b""[..]), 12.5)));
+/// assert_eq!(le_f32(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn f32<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, f32, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_f32,
+ Endianness::Little => le_f32,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_f32,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_f32,
+ }
+ }(input)
+}
+
+/// Recognizes an 8 byte floating point number
+///
+/// If the parameter is `winnow::binary::Endianness::Big`, parse a big endian f64 float,
+/// otherwise if `winnow::binary::Endianness::Little` parse a little endian f64 float.
+///
+/// *Complete version*: returns an error if there is not enough input data
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::binary::f64;
+///
+/// let be_f64 = |s| {
+/// f64(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_f64(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(be_f64(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+///
+/// let le_f64 = |s| {
+/// f64(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_f64(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..]), Ok((&b""[..], 12.5)));
+/// assert_eq!(le_f64(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// # use winnow::Partial;
+/// use winnow::binary::f64;
+///
+/// let be_f64 = |s| {
+/// f64::<_, InputError<_>>(winnow::binary::Endianness::Big).parse_peek(s)
+/// };
+///
+/// assert_eq!(be_f64(Partial::new(&[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])), Ok((Partial::new(&b""[..]), 12.5)));
+/// assert_eq!(be_f64(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(5))));
+///
+/// let le_f64 = |s| {
+/// f64::<_, InputError<_>>(winnow::binary::Endianness::Little).parse_peek(s)
+/// };
+///
+/// assert_eq!(le_f64(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40][..])), Ok((Partial::new(&b""[..]), 12.5)));
+/// assert_eq!(le_f64(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(5))));
+/// ```
+#[inline(always)]
+pub fn f64<I, E: ParserError<I>>(endian: Endianness) -> impl Parser<I, f64, E>
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ <I as Stream>::Slice: AsBytes,
+{
+ move |input: &mut I| {
+ match endian {
+ Endianness::Big => be_f64,
+ Endianness::Little => le_f64,
+ #[cfg(target_endian = "big")]
+ Endianness::Native => be_f64,
+ #[cfg(target_endian = "little")]
+ Endianness::Native => le_f64,
+ }
+ }(input)
+}
+
+/// Gets a number from the parser and returns a
+/// subslice of the input of that size.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Arguments
+/// * `f` The parser to apply.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed, stream::Partial};
+/// # use winnow::prelude::*;
+/// use winnow::Bytes;
+/// use winnow::binary::be_u16;
+/// use winnow::binary::length_data;
+/// use winnow::token::tag;
+///
+/// type Stream<'i> = Partial<&'i Bytes>;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Partial::new(Bytes::new(b))
+/// }
+///
+/// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, &[u8]> {
+/// length_data(be_u16).parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(stream(b"\x00\x03abcefg")), Ok((stream(&b"efg"[..]), &b"abc"[..])));
+/// assert_eq!(parser(stream(b"\x00\x03a")), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+pub fn length_data<I, N, E, F>(mut f: F) -> impl Parser<I, <I as Stream>::Slice, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ N: ToUsize,
+ F: Parser<I, N, E>,
+ E: ParserError<I>,
+{
+ trace("length_data", move |i: &mut I| {
+ let length = f.parse_next(i)?;
+
+ crate::token::take(length).parse_next(i)
+ })
+}
+
+/// Gets a number from the first parser,
+/// takes a subslice of the input of that size,
+/// then applies the second parser on that subslice.
+/// If the second parser returns `Incomplete`,
+/// `length_value` will return an error.
+///
+/// *Complete version*: Returns an error if there is not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there is not enough data.
+///
+/// # Arguments
+/// * `f` The parser to apply.
+/// * `g` The parser to apply on the subslice.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed, stream::{Partial, StreamIsPartial}};
+/// # use winnow::prelude::*;
+/// use winnow::Bytes;
+/// use winnow::binary::be_u16;
+/// use winnow::binary::length_value;
+/// use winnow::token::tag;
+///
+/// type Stream<'i> = Partial<&'i Bytes>;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Partial::new(Bytes::new(b))
+/// }
+///
+/// fn complete_stream(b: &[u8]) -> Stream<'_> {
+/// let mut p = Partial::new(Bytes::new(b));
+/// let _ = p.complete();
+/// p
+/// }
+///
+/// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, &[u8]> {
+/// length_value(be_u16, "abc").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(stream(b"\x00\x03abcefg")), Ok((stream(&b"efg"[..]), &b"abc"[..])));
+/// assert_eq!(parser(stream(b"\x00\x03123123")), Err(ErrMode::Backtrack(InputError::new(complete_stream(&b"123"[..]), ErrorKind::Tag))));
+/// assert_eq!(parser(stream(b"\x00\x03a")), Err(ErrMode::Incomplete(Needed::new(2))));
+/// ```
+pub fn length_value<I, O, N, E, F, G>(mut f: F, mut g: G) -> impl Parser<I, O, E>
+where
+ I: StreamIsPartial,
+ I: Stream + UpdateSlice + Clone,
+ N: ToUsize,
+ F: Parser<I, N, E>,
+ G: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ trace("length_value", move |i: &mut I| {
+ let data = length_data(f.by_ref()).parse_next(i)?;
+ let mut data = I::update_slice(i.clone(), data);
+ let _ = data.complete();
+ let o = g.by_ref().complete_err().parse_next(&mut data)?;
+ Ok(o)
+ })
+}
+
+/// Gets a number from the first parser,
+/// then applies the second parser that many times.
+///
+/// # Arguments
+/// * `f` The parser to apply to obtain the count.
+/// * `g` The parser to apply repeatedly.
+///
+/// # Example
+///
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::Bytes;
+/// use winnow::binary::u8;
+/// use winnow::binary::length_count;
+/// use winnow::token::tag;
+///
+/// type Stream<'i> = &'i Bytes;
+///
+/// fn stream(b: &[u8]) -> Stream<'_> {
+/// Bytes::new(b)
+/// }
+///
+/// fn parser(s: Stream<'_>) -> IResult<Stream<'_>, Vec<&[u8]>> {
+/// length_count(u8.map(|i| {
+/// println!("got number: {}", i);
+/// i
+/// }), "abc").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(stream(b"\x02abcabcabc")), Ok((stream(b"abc"), vec![&b"abc"[..], &b"abc"[..]])));
+/// assert_eq!(parser(stream(b"\x03123123123")), Err(ErrMode::Backtrack(InputError::new(stream(b"123123123"), ErrorKind::Tag))));
+/// # }
+/// ```
+pub fn length_count<I, O, C, N, E, F, G>(mut f: F, mut g: G) -> impl Parser<I, C, E>
+where
+ I: Stream,
+ N: ToUsize,
+ C: Accumulate<O>,
+ F: Parser<I, N, E>,
+ G: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ trace("length_count", move |i: &mut I| {
+ let n = f.parse_next(i)?;
+ let n = n.to_usize();
+ repeat(n, g.by_ref()).parse_next(i)
+ })
+}
diff --git a/src/binary/tests.rs b/src/binary/tests.rs
new file mode 100644
index 0000000..5d92055
--- /dev/null
+++ b/src/binary/tests.rs
@@ -0,0 +1,1270 @@
+use super::*;
+use crate::unpeek;
+use crate::IResult;
+
+mod complete {
+ use super::*;
+ use crate::error::InputError;
+
+ macro_rules! assert_parse(
+ ($left: expr, $right: expr) => {
+ let res: $crate::IResult<_, _, InputError<_>> = $left;
+ assert_eq!(res, $right);
+ };
+ );
+
+ #[test]
+ fn i8_tests() {
+ assert_parse!(i8.parse_peek(&[0x00][..]), Ok((&b""[..], 0)));
+ assert_parse!(i8.parse_peek(&[0x7f][..]), Ok((&b""[..], 127)));
+ assert_parse!(i8.parse_peek(&[0xff][..]), Ok((&b""[..], -1)));
+ assert_parse!(i8.parse_peek(&[0x80][..]), Ok((&b""[..], -128)));
+ }
+
+ #[test]
+ fn be_i8_tests() {
+ assert_parse!(be_i8.parse_peek(&[0x00][..]), Ok((&b""[..], 0)));
+ assert_parse!(be_i8.parse_peek(&[0x7f][..]), Ok((&b""[..], 127)));
+ assert_parse!(be_i8.parse_peek(&[0xff][..]), Ok((&b""[..], -1)));
+ assert_parse!(be_i8.parse_peek(&[0x80][..]), Ok((&b""[..], -128)));
+ }
+
+ #[test]
+ fn be_i16_tests() {
+ assert_parse!(be_i16.parse_peek(&[0x00, 0x00][..]), Ok((&b""[..], 0)));
+ assert_parse!(
+ be_i16.parse_peek(&[0x7f, 0xff][..]),
+ Ok((&b""[..], 32_767_i16))
+ );
+ assert_parse!(be_i16.parse_peek(&[0xff, 0xff][..]), Ok((&b""[..], -1)));
+ assert_parse!(
+ be_i16.parse_peek(&[0x80, 0x00][..]),
+ Ok((&b""[..], -32_768_i16))
+ );
+ }
+
+ #[test]
+ fn be_u24_tests() {
+ assert_parse!(
+ be_u24.parse_peek(&[0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ be_u24.parse_peek(&[0x00, 0xFF, 0xFF][..]),
+ Ok((&b""[..], 65_535_u32))
+ );
+ assert_parse!(
+ be_u24.parse_peek(&[0x12, 0x34, 0x56][..]),
+ Ok((&b""[..], 1_193_046_u32))
+ );
+ }
+
+ #[test]
+ fn be_i24_tests() {
+ assert_parse!(
+ be_i24.parse_peek(&[0xFF, 0xFF, 0xFF][..]),
+ Ok((&b""[..], -1_i32))
+ );
+ assert_parse!(
+ be_i24.parse_peek(&[0xFF, 0x00, 0x00][..]),
+ Ok((&b""[..], -65_536_i32))
+ );
+ assert_parse!(
+ be_i24.parse_peek(&[0xED, 0xCB, 0xAA][..]),
+ Ok((&b""[..], -1_193_046_i32))
+ );
+ }
+
+ #[test]
+ fn be_i32_tests() {
+ assert_parse!(
+ be_i32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ be_i32.parse_peek(&[0x7f, 0xff, 0xff, 0xff][..]),
+ Ok((&b""[..], 2_147_483_647_i32))
+ );
+ assert_parse!(
+ be_i32.parse_peek(&[0xff, 0xff, 0xff, 0xff][..]),
+ Ok((&b""[..], -1))
+ );
+ assert_parse!(
+ be_i32.parse_peek(&[0x80, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], -2_147_483_648_i32))
+ );
+ }
+
+ #[test]
+ fn be_i64_tests() {
+ assert_parse!(
+ be_i64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ be_i64.parse_peek(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]),
+ Ok((&b""[..], 9_223_372_036_854_775_807_i64))
+ );
+ assert_parse!(
+ be_i64.parse_peek(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]),
+ Ok((&b""[..], -1))
+ );
+ assert_parse!(
+ be_i64.parse_peek(&[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], -9_223_372_036_854_775_808_i64))
+ );
+ }
+
+ #[test]
+ fn be_i128_tests() {
+ assert_parse!(
+ be_i128.parse_peek(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ ][..]
+ ),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ be_i128.parse_peek(
+ &[
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+ ][..]
+ ),
+ Ok((
+ &b""[..],
+ 170_141_183_460_469_231_731_687_303_715_884_105_727_i128
+ ))
+ );
+ assert_parse!(
+ be_i128.parse_peek(
+ &[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+ ][..]
+ ),
+ Ok((&b""[..], -1))
+ );
+ assert_parse!(
+ be_i128.parse_peek(
+ &[
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ ][..]
+ ),
+ Ok((
+ &b""[..],
+ -170_141_183_460_469_231_731_687_303_715_884_105_728_i128
+ ))
+ );
+ }
+
+ #[test]
+ fn le_i8_tests() {
+ assert_parse!(le_i8.parse_peek(&[0x00][..]), Ok((&b""[..], 0)));
+ assert_parse!(le_i8.parse_peek(&[0x7f][..]), Ok((&b""[..], 127)));
+ assert_parse!(le_i8.parse_peek(&[0xff][..]), Ok((&b""[..], -1)));
+ assert_parse!(le_i8.parse_peek(&[0x80][..]), Ok((&b""[..], -128)));
+ }
+
+ #[test]
+ fn le_i16_tests() {
+ assert_parse!(le_i16.parse_peek(&[0x00, 0x00][..]), Ok((&b""[..], 0)));
+ assert_parse!(
+ le_i16.parse_peek(&[0xff, 0x7f][..]),
+ Ok((&b""[..], 32_767_i16))
+ );
+ assert_parse!(le_i16.parse_peek(&[0xff, 0xff][..]), Ok((&b""[..], -1)));
+ assert_parse!(
+ le_i16.parse_peek(&[0x00, 0x80][..]),
+ Ok((&b""[..], -32_768_i16))
+ );
+ }
+
+ #[test]
+ fn le_u24_tests() {
+ assert_parse!(
+ le_u24.parse_peek(&[0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ le_u24.parse_peek(&[0xFF, 0xFF, 0x00][..]),
+ Ok((&b""[..], 65_535_u32))
+ );
+ assert_parse!(
+ le_u24.parse_peek(&[0x56, 0x34, 0x12][..]),
+ Ok((&b""[..], 1_193_046_u32))
+ );
+ }
+
+ #[test]
+ fn le_i24_tests() {
+ assert_parse!(
+ le_i24.parse_peek(&[0xFF, 0xFF, 0xFF][..]),
+ Ok((&b""[..], -1_i32))
+ );
+ assert_parse!(
+ le_i24.parse_peek(&[0x00, 0x00, 0xFF][..]),
+ Ok((&b""[..], -65_536_i32))
+ );
+ assert_parse!(
+ le_i24.parse_peek(&[0xAA, 0xCB, 0xED][..]),
+ Ok((&b""[..], -1_193_046_i32))
+ );
+ }
+
+ #[test]
+ fn le_i32_tests() {
+ assert_parse!(
+ le_i32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ le_i32.parse_peek(&[0xff, 0xff, 0xff, 0x7f][..]),
+ Ok((&b""[..], 2_147_483_647_i32))
+ );
+ assert_parse!(
+ le_i32.parse_peek(&[0xff, 0xff, 0xff, 0xff][..]),
+ Ok((&b""[..], -1))
+ );
+ assert_parse!(
+ le_i32.parse_peek(&[0x00, 0x00, 0x00, 0x80][..]),
+ Ok((&b""[..], -2_147_483_648_i32))
+ );
+ }
+
+ #[test]
+ fn le_i64_tests() {
+ assert_parse!(
+ le_i64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ le_i64.parse_peek(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f][..]),
+ Ok((&b""[..], 9_223_372_036_854_775_807_i64))
+ );
+ assert_parse!(
+ le_i64.parse_peek(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]),
+ Ok((&b""[..], -1))
+ );
+ assert_parse!(
+ le_i64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80][..]),
+ Ok((&b""[..], -9_223_372_036_854_775_808_i64))
+ );
+ }
+
+ #[test]
+ fn le_i128_tests() {
+ assert_parse!(
+ le_i128.parse_peek(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ ][..]
+ ),
+ Ok((&b""[..], 0))
+ );
+ assert_parse!(
+ le_i128.parse_peek(
+ &[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x7f
+ ][..]
+ ),
+ Ok((
+ &b""[..],
+ 170_141_183_460_469_231_731_687_303_715_884_105_727_i128
+ ))
+ );
+ assert_parse!(
+ le_i128.parse_peek(
+ &[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+ ][..]
+ ),
+ Ok((&b""[..], -1))
+ );
+ assert_parse!(
+ le_i128.parse_peek(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80
+ ][..]
+ ),
+ Ok((
+ &b""[..],
+ -170_141_183_460_469_231_731_687_303_715_884_105_728_i128
+ ))
+ );
+ }
+
+ #[test]
+ fn be_f32_tests() {
+ assert_parse!(
+ be_f32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0_f32))
+ );
+ assert_parse!(
+ be_f32.parse_peek(&[0x4d, 0x31, 0x1f, 0xd8][..]),
+ Ok((&b""[..], 185_728_380_f32))
+ );
+ }
+
+ #[test]
+ fn be_f64_tests() {
+ assert_parse!(
+ be_f64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0_f64))
+ );
+ assert_parse!(
+ be_f64.parse_peek(&[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 185_728_392_f64))
+ );
+ }
+
+ #[test]
+ fn le_f32_tests() {
+ assert_parse!(
+ le_f32.parse_peek(&[0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0_f32))
+ );
+ assert_parse!(
+ le_f32.parse_peek(&[0xd8, 0x1f, 0x31, 0x4d][..]),
+ Ok((&b""[..], 185_728_380_f32))
+ );
+ }
+
+ #[test]
+ fn le_f64_tests() {
+ assert_parse!(
+ le_f64.parse_peek(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]),
+ Ok((&b""[..], 0_f64))
+ );
+ assert_parse!(
+ le_f64.parse_peek(&[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41][..]),
+ Ok((&b""[..], 185_728_392_f64))
+ );
+ }
+
+ #[test]
+ fn configurable_endianness() {
+ use crate::binary::Endianness;
+
+ fn be_tst16(i: &[u8]) -> IResult<&[u8], u16> {
+ u16(Endianness::Big).parse_peek(i)
+ }
+ fn le_tst16(i: &[u8]) -> IResult<&[u8], u16> {
+ u16(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(be_tst16(&[0x80, 0x00]), Ok((&b""[..], 32_768_u16)));
+ assert_eq!(le_tst16(&[0x80, 0x00]), Ok((&b""[..], 128_u16)));
+
+ fn be_tst32(i: &[u8]) -> IResult<&[u8], u32> {
+ u32(Endianness::Big).parse_peek(i)
+ }
+ fn le_tst32(i: &[u8]) -> IResult<&[u8], u32> {
+ u32(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tst32(&[0x12, 0x00, 0x60, 0x00]),
+ Ok((&b""[..], 302_014_464_u32))
+ );
+ assert_eq!(
+ le_tst32(&[0x12, 0x00, 0x60, 0x00]),
+ Ok((&b""[..], 6_291_474_u32))
+ );
+
+ fn be_tst64(i: &[u8]) -> IResult<&[u8], u64> {
+ u64(Endianness::Big).parse_peek(i)
+ }
+ fn le_tst64(i: &[u8]) -> IResult<&[u8], u64> {
+ u64(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]),
+ Ok((&b""[..], 1_297_142_246_100_992_000_u64))
+ );
+ assert_eq!(
+ le_tst64(&[0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]),
+ Ok((&b""[..], 36_028_874_334_666_770_u64))
+ );
+
+ fn be_tsti16(i: &[u8]) -> IResult<&[u8], i16> {
+ i16(Endianness::Big).parse_peek(i)
+ }
+ fn le_tsti16(i: &[u8]) -> IResult<&[u8], i16> {
+ i16(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(be_tsti16(&[0x00, 0x80]), Ok((&b""[..], 128_i16)));
+ assert_eq!(le_tsti16(&[0x00, 0x80]), Ok((&b""[..], -32_768_i16)));
+
+ fn be_tsti32(i: &[u8]) -> IResult<&[u8], i32> {
+ i32(Endianness::Big).parse_peek(i)
+ }
+ fn le_tsti32(i: &[u8]) -> IResult<&[u8], i32> {
+ i32(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tsti32(&[0x00, 0x12, 0x60, 0x00]),
+ Ok((&b""[..], 1_204_224_i32))
+ );
+ assert_eq!(
+ le_tsti32(&[0x00, 0x12, 0x60, 0x00]),
+ Ok((&b""[..], 6_296_064_i32))
+ );
+
+ fn be_tsti64(i: &[u8]) -> IResult<&[u8], i64> {
+ i64(Endianness::Big).parse_peek(i)
+ }
+ fn le_tsti64(i: &[u8]) -> IResult<&[u8], i64> {
+ i64(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]),
+ Ok((&b""[..], 71_881_672_479_506_432_i64))
+ );
+ assert_eq!(
+ le_tsti64(&[0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00]),
+ Ok((&b""[..], 36_028_874_334_732_032_i64))
+ );
+ }
+}
+
+mod partial {
+ use super::*;
+ use crate::error::ErrMode;
+ use crate::error::InputError;
+ use crate::error::Needed;
+ #[cfg(feature = "alloc")]
+ use crate::lib::std::vec::Vec;
+ use crate::Partial;
+ use crate::{
+ ascii::digit1 as digit,
+ binary::{be_u16, be_u8},
+ error::ErrorKind,
+ lib::std::str::{self, FromStr},
+ IResult,
+ };
+
+ macro_rules! assert_parse(
+ ($left: expr, $right: expr) => {
+ let res: $crate::IResult<_, _, InputError<_>> = $left;
+ assert_eq!(res, $right);
+ };
+ );
+
+ #[test]
+ fn i8_tests() {
+ assert_parse!(
+ be_i8.parse_peek(Partial::new(&[0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ be_i8.parse_peek(Partial::new(&[0x7f][..])),
+ Ok((Partial::new(&b""[..]), 127))
+ );
+ assert_parse!(
+ be_i8.parse_peek(Partial::new(&[0xff][..])),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ be_i8.parse_peek(Partial::new(&[0x80][..])),
+ Ok((Partial::new(&b""[..]), -128))
+ );
+ assert_parse!(
+ be_i8.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn i16_tests() {
+ assert_parse!(
+ be_i16.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ be_i16.parse_peek(Partial::new(&[0x7f, 0xff][..])),
+ Ok((Partial::new(&b""[..]), 32_767_i16))
+ );
+ assert_parse!(
+ be_i16.parse_peek(Partial::new(&[0xff, 0xff][..])),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ be_i16.parse_peek(Partial::new(&[0x80, 0x00][..])),
+ Ok((Partial::new(&b""[..]), -32_768_i16))
+ );
+ assert_parse!(
+ be_i16.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_parse!(
+ be_i16.parse_peek(Partial::new(&[0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn u24_tests() {
+ assert_parse!(
+ be_u24.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ be_u24.parse_peek(Partial::new(&[0x00, 0xFF, 0xFF][..])),
+ Ok((Partial::new(&b""[..]), 65_535_u32))
+ );
+ assert_parse!(
+ be_u24.parse_peek(Partial::new(&[0x12, 0x34, 0x56][..])),
+ Ok((Partial::new(&b""[..]), 1_193_046_u32))
+ );
+ assert_parse!(
+ be_u24.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_parse!(
+ be_u24.parse_peek(Partial::new(&[0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_parse!(
+ be_u24.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn i24_tests() {
+ assert_parse!(
+ be_i24.parse_peek(Partial::new(&[0xFF, 0xFF, 0xFF][..])),
+ Ok((Partial::new(&b""[..]), -1_i32))
+ );
+ assert_parse!(
+ be_i24.parse_peek(Partial::new(&[0xFF, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), -65_536_i32))
+ );
+ assert_parse!(
+ be_i24.parse_peek(Partial::new(&[0xED, 0xCB, 0xAA][..])),
+ Ok((Partial::new(&b""[..]), -1_193_046_i32))
+ );
+ assert_parse!(
+ be_i24.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_parse!(
+ be_i24.parse_peek(Partial::new(&[0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_parse!(
+ be_i24.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn i32_tests() {
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0x7f, 0xff, 0xff, 0xff][..])),
+ Ok((Partial::new(&b""[..]), 2_147_483_647_i32))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0xff][..])),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0x80, 0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), -2_147_483_648_i32))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_parse!(
+ be_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn i64_tests() {
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(
+ &[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]
+ )),
+ Ok((Partial::new(&b""[..]), 9_223_372_036_854_775_807_i64))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(
+ &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]
+ )),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(
+ &[0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Ok((Partial::new(&b""[..]), -9_223_372_036_854_775_808_i64))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(8)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(7)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(6)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(5)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_parse!(
+ be_i64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn i128_tests() {
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ ][..]
+ )),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+ ][..]
+ )),
+ Ok((
+ Partial::new(&b""[..]),
+ 170_141_183_460_469_231_731_687_303_715_884_105_727_i128
+ ))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+ ][..]
+ )),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ ][..]
+ )),
+ Ok((
+ Partial::new(&b""[..]),
+ -170_141_183_460_469_231_731_687_303_715_884_105_728_i128
+ ))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[][..])),
+ Err(ErrMode::Incomplete(Needed::new(16)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(15)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(14)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(13)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(12)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(11)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..])),
+ Err(ErrMode::Incomplete(Needed::new(10)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(9)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(8)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(7)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(6)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(5)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ ][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_parse!(
+ be_i128.parse_peek(Partial::new(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ ][..]
+ )),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ }
+
+ #[test]
+ fn le_i8_tests() {
+ assert_parse!(
+ le_i8.parse_peek(Partial::new(&[0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ le_i8.parse_peek(Partial::new(&[0x7f][..])),
+ Ok((Partial::new(&b""[..]), 127))
+ );
+ assert_parse!(
+ le_i8.parse_peek(Partial::new(&[0xff][..])),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ le_i8.parse_peek(Partial::new(&[0x80][..])),
+ Ok((Partial::new(&b""[..]), -128))
+ );
+ }
+
+ #[test]
+ fn le_i16_tests() {
+ assert_parse!(
+ le_i16.parse_peek(Partial::new(&[0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ le_i16.parse_peek(Partial::new(&[0xff, 0x7f][..])),
+ Ok((Partial::new(&b""[..]), 32_767_i16))
+ );
+ assert_parse!(
+ le_i16.parse_peek(Partial::new(&[0xff, 0xff][..])),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ le_i16.parse_peek(Partial::new(&[0x00, 0x80][..])),
+ Ok((Partial::new(&b""[..]), -32_768_i16))
+ );
+ }
+
+ #[test]
+ fn le_u24_tests() {
+ assert_parse!(
+ le_u24.parse_peek(Partial::new(&[0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ le_u24.parse_peek(Partial::new(&[0xFF, 0xFF, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 65_535_u32))
+ );
+ assert_parse!(
+ le_u24.parse_peek(Partial::new(&[0x56, 0x34, 0x12][..])),
+ Ok((Partial::new(&b""[..]), 1_193_046_u32))
+ );
+ }
+
+ #[test]
+ fn le_i24_tests() {
+ assert_parse!(
+ le_i24.parse_peek(Partial::new(&[0xFF, 0xFF, 0xFF][..])),
+ Ok((Partial::new(&b""[..]), -1_i32))
+ );
+ assert_parse!(
+ le_i24.parse_peek(Partial::new(&[0x00, 0x00, 0xFF][..])),
+ Ok((Partial::new(&b""[..]), -65_536_i32))
+ );
+ assert_parse!(
+ le_i24.parse_peek(Partial::new(&[0xAA, 0xCB, 0xED][..])),
+ Ok((Partial::new(&b""[..]), -1_193_046_i32))
+ );
+ }
+
+ #[test]
+ fn le_i32_tests() {
+ assert_parse!(
+ le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ le_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0x7f][..])),
+ Ok((Partial::new(&b""[..]), 2_147_483_647_i32))
+ );
+ assert_parse!(
+ le_i32.parse_peek(Partial::new(&[0xff, 0xff, 0xff, 0xff][..])),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ le_i32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x80][..])),
+ Ok((Partial::new(&b""[..]), -2_147_483_648_i32))
+ );
+ }
+
+ #[test]
+ fn le_i64_tests() {
+ assert_parse!(
+ le_i64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ le_i64.parse_peek(Partial::new(
+ &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f][..]
+ )),
+ Ok((Partial::new(&b""[..]), 9_223_372_036_854_775_807_i64))
+ );
+ assert_parse!(
+ le_i64.parse_peek(Partial::new(
+ &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]
+ )),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ le_i64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80][..]
+ )),
+ Ok((Partial::new(&b""[..]), -9_223_372_036_854_775_808_i64))
+ );
+ }
+
+ #[test]
+ fn le_i128_tests() {
+ assert_parse!(
+ le_i128.parse_peek(Partial::new(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ ][..]
+ )),
+ Ok((Partial::new(&b""[..]), 0))
+ );
+ assert_parse!(
+ le_i128.parse_peek(Partial::new(
+ &[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x7f
+ ][..]
+ )),
+ Ok((
+ Partial::new(&b""[..]),
+ 170_141_183_460_469_231_731_687_303_715_884_105_727_i128
+ ))
+ );
+ assert_parse!(
+ le_i128.parse_peek(Partial::new(
+ &[
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff
+ ][..]
+ )),
+ Ok((Partial::new(&b""[..]), -1))
+ );
+ assert_parse!(
+ le_i128.parse_peek(Partial::new(
+ &[
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80
+ ][..]
+ )),
+ Ok((
+ Partial::new(&b""[..]),
+ -170_141_183_460_469_231_731_687_303_715_884_105_728_i128
+ ))
+ );
+ }
+
+ #[test]
+ fn be_f32_tests() {
+ assert_parse!(
+ be_f32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0_f32))
+ );
+ assert_parse!(
+ be_f32.parse_peek(Partial::new(&[0x4d, 0x31, 0x1f, 0xd8][..])),
+ Ok((Partial::new(&b""[..]), 185_728_380_f32))
+ );
+ }
+
+ #[test]
+ fn be_f64_tests() {
+ assert_parse!(
+ be_f64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Ok((Partial::new(&b""[..]), 0_f64))
+ );
+ assert_parse!(
+ be_f64.parse_peek(Partial::new(
+ &[0x41, 0xa6, 0x23, 0xfb, 0x10, 0x00, 0x00, 0x00][..]
+ )),
+ Ok((Partial::new(&b""[..]), 185_728_392_f64))
+ );
+ }
+
+ #[test]
+ fn le_f32_tests() {
+ assert_parse!(
+ le_f32.parse_peek(Partial::new(&[0x00, 0x00, 0x00, 0x00][..])),
+ Ok((Partial::new(&b""[..]), 0_f32))
+ );
+ assert_parse!(
+ le_f32.parse_peek(Partial::new(&[0xd8, 0x1f, 0x31, 0x4d][..])),
+ Ok((Partial::new(&b""[..]), 185_728_380_f32))
+ );
+ }
+
+ #[test]
+ fn le_f64_tests() {
+ assert_parse!(
+ le_f64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
+ )),
+ Ok((Partial::new(&b""[..]), 0_f64))
+ );
+ assert_parse!(
+ le_f64.parse_peek(Partial::new(
+ &[0x00, 0x00, 0x00, 0x10, 0xfb, 0x23, 0xa6, 0x41][..]
+ )),
+ Ok((Partial::new(&b""[..]), 185_728_392_f64))
+ );
+ }
+
+ #[test]
+ fn configurable_endianness() {
+ use crate::binary::Endianness;
+
+ fn be_tst16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> {
+ u16(Endianness::Big).parse_peek(i)
+ }
+ fn le_tst16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> {
+ u16(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tst16(Partial::new(&[0x80, 0x00])),
+ Ok((Partial::new(&b""[..]), 32_768_u16))
+ );
+ assert_eq!(
+ le_tst16(Partial::new(&[0x80, 0x00])),
+ Ok((Partial::new(&b""[..]), 128_u16))
+ );
+
+ fn be_tst32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+ u32(Endianness::Big).parse_peek(i)
+ }
+ fn le_tst32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+ u32(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tst32(Partial::new(&[0x12, 0x00, 0x60, 0x00])),
+ Ok((Partial::new(&b""[..]), 302_014_464_u32))
+ );
+ assert_eq!(
+ le_tst32(Partial::new(&[0x12, 0x00, 0x60, 0x00])),
+ Ok((Partial::new(&b""[..]), 6_291_474_u32))
+ );
+
+ fn be_tst64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> {
+ u64(Endianness::Big).parse_peek(i)
+ }
+ fn le_tst64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u64> {
+ u64(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tst64(Partial::new(&[
+ 0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00
+ ])),
+ Ok((Partial::new(&b""[..]), 1_297_142_246_100_992_000_u64))
+ );
+ assert_eq!(
+ le_tst64(Partial::new(&[
+ 0x12, 0x00, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00
+ ])),
+ Ok((Partial::new(&b""[..]), 36_028_874_334_666_770_u64))
+ );
+
+ fn be_tsti16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> {
+ i16(Endianness::Big).parse_peek(i)
+ }
+ fn le_tsti16(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i16> {
+ i16(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tsti16(Partial::new(&[0x00, 0x80])),
+ Ok((Partial::new(&b""[..]), 128_i16))
+ );
+ assert_eq!(
+ le_tsti16(Partial::new(&[0x00, 0x80])),
+ Ok((Partial::new(&b""[..]), -32_768_i16))
+ );
+
+ fn be_tsti32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> {
+ i32(Endianness::Big).parse_peek(i)
+ }
+ fn le_tsti32(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i32> {
+ i32(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tsti32(Partial::new(&[0x00, 0x12, 0x60, 0x00])),
+ Ok((Partial::new(&b""[..]), 1_204_224_i32))
+ );
+ assert_eq!(
+ le_tsti32(Partial::new(&[0x00, 0x12, 0x60, 0x00])),
+ Ok((Partial::new(&b""[..]), 6_296_064_i32))
+ );
+
+ fn be_tsti64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> {
+ i64(Endianness::Big).parse_peek(i)
+ }
+ fn le_tsti64(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, i64> {
+ i64(Endianness::Little).parse_peek(i)
+ }
+ assert_eq!(
+ be_tsti64(Partial::new(&[
+ 0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00
+ ])),
+ Ok((Partial::new(&b""[..]), 71_881_672_479_506_432_i64))
+ );
+ assert_eq!(
+ le_tsti64(Partial::new(&[
+ 0x00, 0xFF, 0x60, 0x00, 0x12, 0x00, 0x80, 0x00
+ ])),
+ Ok((Partial::new(&b""[..]), 36_028_874_334_732_032_i64))
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn length_count_test() {
+ fn number(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+ digit
+ .try_map(str::from_utf8)
+ .try_map(FromStr::from_str)
+ .parse_peek(i)
+ }
+
+ fn cnt(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ length_count(unpeek(number), "abc").parse_peek(i)
+ }
+
+ assert_eq!(
+ cnt(Partial::new(&b"2abcabcabcdef"[..])),
+ Ok((Partial::new(&b"abcdef"[..]), vec![&b"abc"[..], &b"abc"[..]]))
+ );
+ assert_eq!(
+ cnt(Partial::new(&b"2ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ cnt(Partial::new(&b"3abcab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ cnt(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ cnt(Partial::new(&b"2abcxxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ }
+
+ #[test]
+ fn length_data_test() {
+ fn number(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u32> {
+ digit
+ .try_map(str::from_utf8)
+ .try_map(FromStr::from_str)
+ .parse_peek(i)
+ }
+
+ fn take(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ length_data(unpeek(number)).parse_peek(i)
+ }
+
+ assert_eq!(
+ take(Partial::new(&b"6abcabcabcdef"[..])),
+ Ok((Partial::new(&b"abcdef"[..]), &b"abcabc"[..]))
+ );
+ assert_eq!(
+ take(Partial::new(&b"3ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ take(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ take(Partial::new(&b"2abcxxx"[..])),
+ Ok((Partial::new(&b"cxxx"[..]), &b"ab"[..]))
+ );
+ }
+
+ #[test]
+ fn length_value_test() {
+ use crate::stream::StreamIsPartial;
+
+ fn length_value_1(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u16> {
+ length_value(be_u8, be_u16).parse_peek(i)
+ }
+ fn length_value_2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (u8, u8)> {
+ length_value(be_u8, (be_u8, be_u8)).parse_peek(i)
+ }
+
+ let mut empty_complete = Partial::new(&b""[..]);
+ let _ = empty_complete.complete();
+
+ let i1 = [0, 5, 6];
+ assert_eq!(
+ length_value_1(Partial::new(&i1)),
+ Err(ErrMode::Backtrack(error_position!(
+ &empty_complete,
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ length_value_2(Partial::new(&i1)),
+ Err(ErrMode::Backtrack(error_position!(
+ &empty_complete,
+ ErrorKind::Token
+ )))
+ );
+
+ let i2 = [1, 5, 6, 3];
+ {
+ let mut middle_complete = Partial::new(&i2[1..2]);
+ let _ = middle_complete.complete();
+ assert_eq!(
+ length_value_1(Partial::new(&i2)),
+ Err(ErrMode::Backtrack(error_position!(
+ &middle_complete,
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ length_value_2(Partial::new(&i2)),
+ Err(ErrMode::Backtrack(error_position!(
+ &empty_complete,
+ ErrorKind::Token
+ )))
+ );
+ }
+
+ let i3 = [2, 5, 6, 3, 4, 5, 7];
+ assert_eq!(
+ length_value_1(Partial::new(&i3)),
+ Ok((Partial::new(&i3[3..]), 1286))
+ );
+ assert_eq!(
+ length_value_2(Partial::new(&i3)),
+ Ok((Partial::new(&i3[3..]), (5, 6)))
+ );
+
+ let i4 = [3, 5, 6, 3, 4, 5];
+ assert_eq!(
+ length_value_1(Partial::new(&i4)),
+ Ok((Partial::new(&i4[4..]), 1286))
+ );
+ assert_eq!(
+ length_value_2(Partial::new(&i4)),
+ Ok((Partial::new(&i4[4..]), (5, 6)))
+ );
+ }
+}
diff --git a/src/combinator/branch.rs b/src/combinator/branch.rs
new file mode 100644
index 0000000..b909ff1
--- /dev/null
+++ b/src/combinator/branch.rs
@@ -0,0 +1,297 @@
+use crate::error::{ErrMode, ErrorKind, ParserError};
+use crate::stream::Stream;
+use crate::trace::trace;
+use crate::*;
+
+#[doc(inline)]
+pub use crate::dispatch;
+
+/// Helper trait for the [alt()] combinator.
+///
+/// This trait is implemented for tuples of up to 21 elements
+pub trait Alt<I, O, E> {
+ /// Tests each parser in the tuple and returns the result of the first one that succeeds
+ fn choice(&mut self, input: &mut I) -> PResult<O, E>;
+}
+
+/// Pick the first successful parser
+///
+/// For tight control over the error, add a final case using [`fail`][crate::combinator::fail].
+/// Alternatively, with a [custom error type][crate::_topic::error], it is possible to track all
+/// errors or return the error of the parser that went the farthest in the input data.
+///
+/// When the alternative cases have unique prefixes, [`dispatch`] can offer better performance.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::InputError,error::ErrorKind, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::ascii::{alpha1, digit1};
+/// use winnow::combinator::alt;
+/// # fn main() {
+/// fn parser(input: &str) -> IResult<&str, &str> {
+/// alt((alpha1, digit1)).parse_peek(input)
+/// };
+///
+/// // the first parser, alpha1, recognizes the input
+/// assert_eq!(parser("abc"), Ok(("", "abc")));
+///
+/// // the first parser returns an error, so alt tries the second one
+/// assert_eq!(parser("123456"), Ok(("", "123456")));
+///
+/// // both parsers failed, and with the default error type, alt will return the last error
+/// assert_eq!(parser(" "), Err(ErrMode::Backtrack(InputError::new(" ", ErrorKind::Slice))));
+/// # }
+/// ```
+#[doc(alias = "choice")]
+pub fn alt<I: Stream, O, E: ParserError<I>, List: Alt<I, O, E>>(
+ mut l: List,
+) -> impl Parser<I, O, E> {
+ trace("alt", move |i: &mut I| l.choice(i))
+}
+
+/// Helper trait for the [permutation()] combinator.
+///
+/// This trait is implemented for tuples of up to 21 elements
+pub trait Permutation<I, O, E> {
+ /// Tries to apply all parsers in the tuple in various orders until all of them succeed
+ fn permutation(&mut self, input: &mut I) -> PResult<O, E>;
+}
+
+/// Applies a list of parsers in any order.
+///
+/// Permutation will succeed if all of the child parsers succeeded.
+/// It takes as argument a tuple of parsers, and returns a
+/// tuple of the parser results.
+///
+/// ```rust
+/// # use winnow::{error::ErrMode,error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::ascii::{alpha1, digit1};
+/// use winnow::combinator::permutation;
+/// # fn main() {
+/// fn parser(input: &str) -> IResult<&str, (&str, &str)> {
+/// permutation((alpha1, digit1)).parse_peek(input)
+/// }
+///
+/// // permutation recognizes alphabetic characters then digit
+/// assert_eq!(parser("abc123"), Ok(("", ("abc", "123"))));
+///
+/// // but also in inverse order
+/// assert_eq!(parser("123abc"), Ok(("", ("abc", "123"))));
+///
+/// // it will fail if one of the parsers failed
+/// assert_eq!(parser("abc;"), Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Slice))));
+/// # }
+/// ```
+///
+/// The parsers are applied greedily: if there are multiple unapplied parsers
+/// that could parse the next slice of input, the first one is used.
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::permutation;
+/// use winnow::token::any;
+///
+/// fn parser(input: &str) -> IResult<&str, (char, char)> {
+/// permutation((any, 'a')).parse_peek(input)
+/// }
+///
+/// // any parses 'b', then char('a') parses 'a'
+/// assert_eq!(parser("ba"), Ok(("", ('b', 'a'))));
+///
+/// // any parses 'a', then char('a') fails on 'b',
+/// // even though char('a') followed by any would succeed
+/// assert_eq!(parser("ab"), Err(ErrMode::Backtrack(InputError::new("b", ErrorKind::Verify))));
+/// ```
+///
+pub fn permutation<I: Stream, O, E: ParserError<I>, List: Permutation<I, O, E>>(
+ mut l: List,
+) -> impl Parser<I, O, E> {
+ trace("permutation", move |i: &mut I| l.permutation(i))
+}
+
+impl<const N: usize, I: Stream, O, E: ParserError<I>, P: Parser<I, O, E>> Alt<I, O, E> for [P; N] {
+ fn choice(&mut self, input: &mut I) -> PResult<O, E> {
+ let mut error: Option<E> = None;
+
+ let start = input.checkpoint();
+ for branch in self {
+ input.reset(start.clone());
+ match branch.parse_next(input) {
+ Err(ErrMode::Backtrack(e)) => {
+ error = match error {
+ Some(error) => Some(error.or(e)),
+ None => Some(e),
+ };
+ }
+ res => return res,
+ }
+ }
+
+ match error {
+ Some(e) => Err(ErrMode::Backtrack(e.append(input, ErrorKind::Alt))),
+ None => Err(ErrMode::assert(input, "`alt` needs at least one parser")),
+ }
+ }
+}
+
+macro_rules! alt_trait(
+ ($first:ident $second:ident $($id: ident)+) => (
+ alt_trait!(__impl $first $second; $($id)+);
+ );
+ (__impl $($current:ident)*; $head:ident $($id: ident)+) => (
+ alt_trait_impl!($($current)*);
+
+ alt_trait!(__impl $($current)* $head; $($id)+);
+ );
+ (__impl $($current:ident)*; $head:ident) => (
+ alt_trait_impl!($($current)*);
+ alt_trait_impl!($($current)* $head);
+ );
+);
+
+macro_rules! alt_trait_impl(
+ ($($id:ident)+) => (
+ impl<
+ I: Stream, Output, Error: ParserError<I>,
+ $($id: Parser<I, Output, Error>),+
+ > Alt<I, Output, Error> for ( $($id),+ ) {
+
+ fn choice(&mut self, input: &mut I) -> PResult<Output, Error> {
+ let start = input.checkpoint();
+ match self.0.parse_next(input) {
+ Err(ErrMode::Backtrack(e)) => alt_trait_inner!(1, self, input, start, e, $($id)+),
+ res => res,
+ }
+ }
+ }
+ );
+);
+
+macro_rules! alt_trait_inner(
+ ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident $($id:ident)+) => ({
+ $input.reset($start.clone());
+ match $self.$it.parse_next($input) {
+ Err(ErrMode::Backtrack(e)) => {
+ let err = $err.or(e);
+ succ!($it, alt_trait_inner!($self, $input, $start, err, $($id)+))
+ }
+ res => res,
+ }
+ });
+ ($it:tt, $self:expr, $input:expr, $start:ident, $err:expr, $head:ident) => ({
+ Err(ErrMode::Backtrack($err.append($input, ErrorKind::Alt)))
+ });
+);
+
+alt_trait!(Alt2 Alt3 Alt4 Alt5 Alt6 Alt7 Alt8 Alt9 Alt10 Alt11 Alt12 Alt13 Alt14 Alt15 Alt16 Alt17 Alt18 Alt19 Alt20 Alt21 Alt22);
+
+// Manually implement Alt for (A,), the 1-tuple type
+impl<I, O, E: ParserError<I>, A: Parser<I, O, E>> Alt<I, O, E> for (A,) {
+ fn choice(&mut self, input: &mut I) -> PResult<O, E> {
+ self.0.parse_next(input)
+ }
+}
+
+macro_rules! permutation_trait(
+ (
+ $name1:ident $ty1:ident $item1:ident
+ $name2:ident $ty2:ident $item2:ident
+ $($name3:ident $ty3:ident $item3:ident)*
+ ) => (
+ permutation_trait!(__impl $name1 $ty1 $item1, $name2 $ty2 $item2; $($name3 $ty3 $item3)*);
+ );
+ (
+ __impl $($name:ident $ty:ident $item:ident),+;
+ $name1:ident $ty1:ident $item1:ident $($name2:ident $ty2:ident $item2:ident)*
+ ) => (
+ permutation_trait_impl!($($name $ty $item),+);
+ permutation_trait!(__impl $($name $ty $item),+ , $name1 $ty1 $item1; $($name2 $ty2 $item2)*);
+ );
+ (__impl $($name:ident $ty:ident $item:ident),+;) => (
+ permutation_trait_impl!($($name $ty $item),+);
+ );
+);
+
+macro_rules! permutation_trait_impl(
+ ($($name:ident $ty:ident $item:ident),+) => (
+ impl<
+ I: Stream, $($ty),+ , Error: ParserError<I>,
+ $($name: Parser<I, $ty, Error>),+
+ > Permutation<I, ( $($ty),+ ), Error> for ( $($name),+ ) {
+
+ fn permutation(&mut self, input: &mut I) -> PResult<( $($ty),+ ), Error> {
+ let mut res = ($(Option::<$ty>::None),+);
+
+ loop {
+ let mut err: Option<Error> = None;
+ let start = input.checkpoint();
+ permutation_trait_inner!(0, self, input, start, res, err, $($name)+);
+
+ // If we reach here, every iterator has either been applied before,
+ // or errored on the remaining input
+ if let Some(err) = err {
+ // There are remaining parsers, and all errored on the remaining input
+ input.reset(start.clone());
+ return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Alt)));
+ }
+
+ // All parsers were applied
+ match res {
+ ($(Some($item)),+) => return Ok(($($item),+)),
+ _ => unreachable!(),
+ }
+ }
+ }
+ }
+ );
+);
+
+macro_rules! permutation_trait_inner(
+ ($it:tt, $self:expr, $input:ident, $start:ident, $res:expr, $err:expr, $head:ident $($id:ident)*) => (
+ if $res.$it.is_none() {
+ $input.reset($start.clone());
+ match $self.$it.parse_next($input) {
+ Ok(o) => {
+ $res.$it = Some(o);
+ continue;
+ }
+ Err(ErrMode::Backtrack(e)) => {
+ $err = Some(match $err {
+ Some(err) => err.or(e),
+ None => e,
+ });
+ }
+ Err(e) => return Err(e),
+ };
+ }
+ succ!($it, permutation_trait_inner!($self, $input, $start, $res, $err, $($id)*));
+ );
+ ($it:tt, $self:expr, $input:ident, $start:ident, $res:expr, $err:expr,) => ();
+);
+
+permutation_trait!(
+ P1 O1 o1
+ P2 O2 o2
+ P3 O3 o3
+ P4 O4 o4
+ P5 O5 o5
+ P6 O6 o6
+ P7 O7 o7
+ P8 O8 o8
+ P9 O9 o9
+ P10 O10 o10
+ P11 O11 o11
+ P12 O12 o12
+ P13 O13 o13
+ P14 O14 o14
+ P15 O15 o15
+ P16 O16 o16
+ P17 O17 o17
+ P18 O18 o18
+ P19 O19 o19
+ P20 O20 o20
+ P21 O21 o21
+);
diff --git a/src/combinator/core.rs b/src/combinator/core.rs
new file mode 100644
index 0000000..d784b4e
--- /dev/null
+++ b/src/combinator/core.rs
@@ -0,0 +1,491 @@
+use crate::error::{ErrMode, ErrorKind, Needed, ParserError};
+use crate::stream::Stream;
+use crate::trace::trace;
+use crate::*;
+
+/// Return the remaining input.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::error::ErrorKind;
+/// # use winnow::error::InputError;
+/// use winnow::combinator::rest;
+/// assert_eq!(rest::<_,InputError<_>>.parse_peek("abc"), Ok(("", "abc")));
+/// assert_eq!(rest::<_,InputError<_>>.parse_peek(""), Ok(("", "")));
+/// ```
+#[inline]
+pub fn rest<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: Stream,
+{
+ trace("rest", move |input: &mut I| Ok(input.finish())).parse_next(input)
+}
+
+/// Return the length of the remaining input.
+///
+/// Note: this does not advance the [`Stream`]
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::error::ErrorKind;
+/// # use winnow::error::InputError;
+/// use winnow::combinator::rest_len;
+/// assert_eq!(rest_len::<_,InputError<_>>.parse_peek("abc"), Ok(("abc", 3)));
+/// assert_eq!(rest_len::<_,InputError<_>>.parse_peek(""), Ok(("", 0)));
+/// ```
+#[inline]
+pub fn rest_len<I, E: ParserError<I>>(input: &mut I) -> PResult<usize, E>
+where
+ I: Stream,
+{
+ trace("rest_len", move |input: &mut I| {
+ let len = input.eof_offset();
+ Ok(len)
+ })
+ .parse_next(input)
+}
+
+/// Apply a [`Parser`], producing `None` on [`ErrMode::Backtrack`].
+///
+/// To chain an error up, see [`cut_err`].
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::opt;
+/// use winnow::ascii::alpha1;
+/// # fn main() {
+///
+/// fn parser(i: &str) -> IResult<&str, Option<&str>> {
+/// opt(alpha1).parse_peek(i)
+/// }
+///
+/// assert_eq!(parser("abcd;"), Ok((";", Some("abcd"))));
+/// assert_eq!(parser("123;"), Ok(("123;", None)));
+/// # }
+/// ```
+pub fn opt<I: Stream, O, E: ParserError<I>, F>(mut f: F) -> impl Parser<I, Option<O>, E>
+where
+ F: Parser<I, O, E>,
+{
+ trace("opt", move |input: &mut I| {
+ let start = input.checkpoint();
+ match f.parse_next(input) {
+ Ok(o) => Ok(Some(o)),
+ Err(ErrMode::Backtrack(_)) => {
+ input.reset(start);
+ Ok(None)
+ }
+ Err(e) => Err(e),
+ }
+ })
+}
+
+/// Calls the parser if the condition is met.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, IResult};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::cond;
+/// use winnow::ascii::alpha1;
+/// # fn main() {
+///
+/// fn parser(b: bool, i: &str) -> IResult<&str, Option<&str>> {
+/// cond(b, alpha1).parse_peek(i)
+/// }
+///
+/// assert_eq!(parser(true, "abcd;"), Ok((";", Some("abcd"))));
+/// assert_eq!(parser(false, "abcd;"), Ok(("abcd;", None)));
+/// assert_eq!(parser(true, "123;"), Err(ErrMode::Backtrack(InputError::new("123;", ErrorKind::Slice))));
+/// assert_eq!(parser(false, "123;"), Ok(("123;", None)));
+/// # }
+/// ```
+pub fn cond<I, O, E: ParserError<I>, F>(b: bool, mut f: F) -> impl Parser<I, Option<O>, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+{
+ trace("cond", move |input: &mut I| {
+ if b {
+ f.parse_next(input).map(Some)
+ } else {
+ Ok(None)
+ }
+ })
+}
+
+/// Tries to apply its parser without consuming the input.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::peek;
+/// use winnow::ascii::alpha1;
+/// # fn main() {
+///
+/// let mut parser = peek(alpha1);
+///
+/// assert_eq!(parser.parse_peek("abcd;"), Ok(("abcd;", "abcd")));
+/// assert_eq!(parser.parse_peek("123;"), Err(ErrMode::Backtrack(InputError::new("123;", ErrorKind::Slice))));
+/// # }
+/// ```
+#[doc(alias = "look_ahead")]
+#[doc(alias = "rewind")]
+pub fn peek<I: Stream, O, E: ParserError<I>, F>(mut f: F) -> impl Parser<I, O, E>
+where
+ F: Parser<I, O, E>,
+{
+ trace("peek", move |input: &mut I| {
+ let start = input.checkpoint();
+ let res = f.parse_next(input);
+ input.reset(start);
+ res
+ })
+}
+
+/// Match the end of the [`Stream`]
+///
+/// Otherwise, it will error.
+///
+/// # Example
+///
+/// ```rust
+/// # use std::str;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::combinator::eof;
+/// # use winnow::prelude::*;
+///
+/// let mut parser = eof;
+/// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Eof))));
+/// assert_eq!(parser.parse_peek(""), Ok(("", "")));
+/// ```
+#[doc(alias = "end")]
+#[doc(alias = "eoi")]
+pub fn eof<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Slice, E>
+where
+ I: Stream,
+{
+ trace("eof", move |input: &mut I| {
+ if input.eof_offset() == 0 {
+ Ok(input.next_slice(0))
+ } else {
+ Err(ErrMode::from_error_kind(input, ErrorKind::Eof))
+ }
+ })
+ .parse_next(input)
+}
+
+/// Succeeds if the child parser returns an error.
+///
+/// **Note:** This does not advance the [`Stream`]
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::not;
+/// use winnow::ascii::alpha1;
+/// # fn main() {
+///
+/// let mut parser = not(alpha1);
+///
+/// assert_eq!(parser.parse_peek("123"), Ok(("123", ())));
+/// assert_eq!(parser.parse_peek("abcd"), Err(ErrMode::Backtrack(InputError::new("abcd", ErrorKind::Not))));
+/// # }
+/// ```
+pub fn not<I: Stream, O, E: ParserError<I>, F>(mut parser: F) -> impl Parser<I, (), E>
+where
+ F: Parser<I, O, E>,
+{
+ trace("not", move |input: &mut I| {
+ let start = input.checkpoint();
+ let res = parser.parse_next(input);
+ input.reset(start);
+ match res {
+ Ok(_) => Err(ErrMode::from_error_kind(input, ErrorKind::Not)),
+ Err(ErrMode::Backtrack(_)) => Ok(()),
+ Err(e) => Err(e),
+ }
+ })
+}
+
+/// Transforms an [`ErrMode::Backtrack`] (recoverable) to [`ErrMode::Cut`] (unrecoverable)
+///
+/// This commits the parse result, preventing alternative branch paths like with
+/// [`winnow::combinator::alt`][crate::combinator::alt].
+///
+/// # Example
+///
+/// Without `cut_err`:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::token::one_of;
+/// # use winnow::ascii::digit1;
+/// # use winnow::combinator::rest;
+/// # use winnow::combinator::alt;
+/// # use winnow::combinator::preceded;
+/// # use winnow::prelude::*;
+/// # fn main() {
+///
+/// fn parser(input: &str) -> IResult<&str, &str> {
+/// alt((
+/// preceded(one_of(['+', '-']), digit1),
+/// rest
+/// )).parse_peek(input)
+/// }
+///
+/// assert_eq!(parser("+10 ab"), Ok((" ab", "10")));
+/// assert_eq!(parser("ab"), Ok(("", "ab")));
+/// assert_eq!(parser("+"), Ok(("", "+")));
+/// # }
+/// ```
+///
+/// With `cut_err`:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::prelude::*;
+/// # use winnow::token::one_of;
+/// # use winnow::ascii::digit1;
+/// # use winnow::combinator::rest;
+/// # use winnow::combinator::alt;
+/// # use winnow::combinator::preceded;
+/// use winnow::combinator::cut_err;
+/// # fn main() {
+///
+/// fn parser(input: &str) -> IResult<&str, &str> {
+/// alt((
+/// preceded(one_of(['+', '-']), cut_err(digit1)),
+/// rest
+/// )).parse_peek(input)
+/// }
+///
+/// assert_eq!(parser("+10 ab"), Ok((" ab", "10")));
+/// assert_eq!(parser("ab"), Ok(("", "ab")));
+/// assert_eq!(parser("+"), Err(ErrMode::Cut(InputError::new("", ErrorKind::Slice ))));
+/// # }
+/// ```
+pub fn cut_err<I, O, E: ParserError<I>, F>(mut parser: F) -> impl Parser<I, O, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+{
+ trace("cut_err", move |input: &mut I| {
+ parser.parse_next(input).map_err(|e| e.cut())
+ })
+}
+
+/// Transforms an [`ErrMode::Cut`] (unrecoverable) to [`ErrMode::Backtrack`] (recoverable)
+///
+/// This attempts the parse, allowing other parsers to be tried on failure, like with
+/// [`winnow::combinator::alt`][crate::combinator::alt].
+pub fn backtrack_err<I, O, E: ParserError<I>, F>(mut parser: F) -> impl Parser<I, O, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+{
+ trace("backtrack_err", move |input: &mut I| {
+ parser.parse_next(input).map_err(|e| e.backtrack())
+ })
+}
+
+/// A placeholder for a not-yet-implemented [`Parser`]
+///
+/// This is analogous to the [`todo!`] macro and helps with prototyping.
+///
+/// # Panic
+///
+/// This will panic when parsing
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::combinator::todo;
+///
+/// fn parser(input: &mut &str) -> PResult<u64> {
+/// todo(input)
+/// }
+/// ```
+#[track_caller]
+pub fn todo<I, O, E>(input: &mut I) -> PResult<O, E>
+where
+ I: Stream,
+{
+ #![allow(clippy::todo)]
+ trace("todo", move |_input: &mut I| todo!("unimplemented parse")).parse_next(input)
+}
+
+/// Repeats the embedded parser, lazily returning the results
+///
+/// Call the iterator's [`ParserIterator::finish`] method to get the remaining input if successful,
+/// or the error value if we encountered an error.
+///
+/// On [`ErrMode::Backtrack`], iteration will stop. To instead chain an error up, see [`cut_err`].
+///
+/// # Example
+///
+/// ```rust
+/// use winnow::{combinator::iterator, IResult, token::tag, ascii::alpha1, combinator::terminated};
+/// use std::collections::HashMap;
+///
+/// let data = "abc|defg|hijkl|mnopqr|123";
+/// let mut it = iterator(data, terminated(alpha1, "|"));
+///
+/// let parsed = it.map(|v| (v, v.len())).collect::<HashMap<_,_>>();
+/// let res: IResult<_,_> = it.finish();
+///
+/// assert_eq!(parsed, [("abc", 3usize), ("defg", 4), ("hijkl", 5), ("mnopqr", 6)].iter().cloned().collect());
+/// assert_eq!(res, Ok(("123", ())));
+/// ```
+pub fn iterator<I, O, E, F>(input: I, parser: F) -> ParserIterator<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+ E: ParserError<I>,
+{
+ ParserIterator {
+ parser,
+ input,
+ state: Some(State::Running),
+ o: Default::default(),
+ }
+}
+
+/// Main structure associated to [`iterator`].
+pub struct ParserIterator<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ parser: F,
+ input: I,
+ state: Option<State<E>>,
+ o: core::marker::PhantomData<O>,
+}
+
+impl<F, I, O, E> ParserIterator<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ /// Returns the remaining input if parsing was successful, or the error if we encountered an error.
+ pub fn finish(mut self) -> PResult<(I, ()), E> {
+ match self.state.take().unwrap() {
+ State::Running | State::Done => Ok((self.input, ())),
+ State::Failure(e) => Err(ErrMode::Cut(e)),
+ State::Incomplete(i) => Err(ErrMode::Incomplete(i)),
+ }
+ }
+}
+
+impl<'a, F, I, O, E> core::iter::Iterator for &'a mut ParserIterator<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ type Item = O;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if let State::Running = self.state.take().unwrap() {
+ let start = self.input.checkpoint();
+
+ match self.parser.parse_next(&mut self.input) {
+ Ok(o) => {
+ self.state = Some(State::Running);
+ Some(o)
+ }
+ Err(ErrMode::Backtrack(_)) => {
+ self.input.reset(start);
+ self.state = Some(State::Done);
+ None
+ }
+ Err(ErrMode::Cut(e)) => {
+ self.state = Some(State::Failure(e));
+ None
+ }
+ Err(ErrMode::Incomplete(i)) => {
+ self.state = Some(State::Incomplete(i));
+ None
+ }
+ }
+ } else {
+ None
+ }
+ }
+}
+
+enum State<E> {
+ Running,
+ Done,
+ Failure(E),
+ Incomplete(Needed),
+}
+
+/// Always succeeds with given value without consuming any input.
+///
+/// For example, it can be used as the last alternative in `alt` to
+/// specify the default case.
+///
+/// **Note:** This never advances the [`Stream`]
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::alt;
+/// use winnow::combinator::success;
+///
+/// let mut parser = success::<_,_,InputError<_>>(10);
+/// assert_eq!(parser.parse_peek("xyz"), Ok(("xyz", 10)));
+///
+/// fn sign(input: &str) -> IResult<&str, isize> {
+/// alt((
+/// '-'.value(-1),
+/// '+'.value(1),
+/// success::<_,_,InputError<_>>(1)
+/// )).parse_peek(input)
+/// }
+/// assert_eq!(sign("+10"), Ok(("10", 1)));
+/// assert_eq!(sign("-10"), Ok(("10", -1)));
+/// assert_eq!(sign("10"), Ok(("10", 1)));
+/// ```
+#[doc(alias = "value")]
+#[doc(alias = "empty")]
+pub fn success<I: Stream, O: Clone, E: ParserError<I>>(val: O) -> impl Parser<I, O, E> {
+ trace("success", move |_input: &mut I| Ok(val.clone()))
+}
+
+/// A parser which always fails.
+///
+/// For example, it can be used as the last alternative in `alt` to
+/// control the error message given.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, IResult};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::fail;
+///
+/// let s = "string";
+/// assert_eq!(fail::<_, &str, _>.parse_peek(s), Err(ErrMode::Backtrack(InputError::new(s, ErrorKind::Fail))));
+/// ```
+#[doc(alias = "unexpected")]
+pub fn fail<I: Stream, O, E: ParserError<I>>(i: &mut I) -> PResult<O, E> {
+ trace("fail", |i: &mut I| {
+ Err(ErrMode::from_error_kind(i, ErrorKind::Fail))
+ })
+ .parse_next(i)
+}
diff --git a/src/combinator/mod.rs b/src/combinator/mod.rs
new file mode 100644
index 0000000..ec68e48
--- /dev/null
+++ b/src/combinator/mod.rs
@@ -0,0 +1,172 @@
+//! # List of parsers and combinators
+//!
+//! **Note**: this list is meant to provide a nicer way to find a parser than reading through the documentation on docs.rs. Function combinators are organized in module so they are a bit easier to find.
+//!
+//! ## Basic elements
+//!
+//! Those are used to recognize the lowest level elements of your grammar, like, "here is a dot", or "here is an big endian integer".
+//!
+//! | combinator | usage | input | output | comment |
+//! |---|---|---|---|---|
+//! | [`one_of`][crate::token::one_of] | `one_of(['a', 'b', 'c'])` | `"abc"` | `Ok(("bc", 'a'))` |Matches one of the provided characters (works with non ASCII characters too)|
+//! | [`none_of`][crate::token::none_of] | `none_of(['a', 'b', 'c'])` | `"xyab"` | `Ok(("yab", 'x'))` |Matches anything but the provided characters|
+//! | [`tag`][crate::token::tag] | `"hello"` | `"hello world"` | `Ok((" world", "hello"))` |Recognizes a specific suite of characters or bytes|
+//! | [`tag_no_case`][crate::token::tag_no_case] | `tag_no_case("hello")` | `"HeLLo World"` | `Ok((" World", "HeLLo"))` |Case insensitive comparison. Note that case insensitive comparison is not well defined for unicode, and that you might have bad surprises|
+//! | [`take`][crate::token::take] | `take(4)` | `"hello"` | `Ok(("o", "hell"))` |Takes a specific number of bytes or characters|
+//! | [`take_while`][crate::token::take_while] | `take_while(0.., is_alphabetic)` | `"abc123"` | `Ok(("123", "abc"))` |Returns the longest list of bytes for which the provided pattern matches.|
+//! | [`take_till0`][crate::token::take_till0] | `take_till0(is_alphabetic)` | `"123abc"` | `Ok(("abc", "123"))` |Returns the longest list of bytes or characters until the provided pattern matches. `take_till1` does the same, but must return at least one character. This is the reverse behaviour from `take_while`: `take_till(f)` is equivalent to `take_while(0.., \|c\| !f(c))`|
+//! | [`take_until0`][crate::token::take_until0] | `take_until0("world")` | `"Hello world"` | `Ok(("world", "Hello "))` |Returns the longest list of bytes or characters until the provided tag is found. `take_until1` does the same, but must return at least one character|
+//!
+//! ## Choice combinators
+//!
+//! | combinator | usage | input | output | comment |
+//! |---|---|---|---|---|
+//! | [`alt`][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;
diff --git a/src/combinator/multi.rs b/src/combinator/multi.rs
new file mode 100644
index 0000000..1fdb753
--- /dev/null
+++ b/src/combinator/multi.rs
@@ -0,0 +1,945 @@
+//! Combinators applying their child parser multiple times
+
+use crate::error::ErrMode;
+use crate::error::ErrorKind;
+use crate::error::ParserError;
+use crate::stream::Accumulate;
+use crate::stream::Range;
+use crate::stream::Stream;
+use crate::trace::trace;
+use crate::PResult;
+use crate::Parser;
+
+/// [`Accumulate`] the output of a parser into a container, like `Vec`
+///
+/// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
+/// [`cut_err`][crate::combinator::cut_err].
+///
+/// # Arguments
+/// * `m` The minimum number of iterations.
+/// * `n` The maximum number of iterations.
+/// * `f` The parser to apply.
+///
+/// To recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`].
+///
+/// **Warning:** If the parser passed to `repeat` accepts empty inputs
+/// (like `alpha0` or `digit0`), `repeat` will return an error,
+/// to prevent going into an infinite loop.
+///
+/// # Example
+///
+/// Zero or more reptitions:
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// repeat(0.., "abc").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+/// assert_eq!(parser(""), Ok(("", vec![])));
+/// # }
+/// ```
+///
+/// One or more reptitions:
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// repeat(1.., "abc").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// # }
+/// ```
+///
+/// Fixed number of repeitions:
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// repeat(2, "abc").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
+/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"])));
+/// # }
+/// ```
+///
+/// Arbitrary reptitions:
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// repeat(0..=2, "abc").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+/// assert_eq!(parser(""), Ok(("", vec![])));
+/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"])));
+/// # }
+/// ```
+#[doc(alias = "many0")]
+#[doc(alias = "count")]
+#[doc(alias = "many0_count")]
+#[doc(alias = "many1")]
+#[doc(alias = "many1_count")]
+#[doc(alias = "many_m_n")]
+#[doc(alias = "repeated")]
+#[doc(alias = "skip_many")]
+#[doc(alias = "skip_many1")]
+#[inline(always)]
+pub fn repeat<I, O, C, E, F>(range: impl Into<Range>, mut f: F) -> impl Parser<I, C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ F: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ let Range {
+ start_inclusive,
+ end_inclusive,
+ } = range.into();
+ trace("repeat", move |i: &mut I| {
+ match (start_inclusive, end_inclusive) {
+ (0, None) => repeat0_(&mut f, i),
+ (1, None) => repeat1_(&mut f, i),
+ (start, end) if Some(start) == end => repeat_n_(start, &mut f, i),
+ (start, end) => repeat_m_n_(start, end.unwrap_or(usize::MAX), &mut f, i),
+ }
+ })
+}
+
+fn repeat0_<I, O, C, E, F>(f: &mut F, i: &mut I) -> PResult<C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ F: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ let mut acc = C::initial(None);
+ loop {
+ let start = i.checkpoint();
+ let len = i.eof_offset();
+ match f.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(acc);
+ }
+ Err(e) => return Err(e),
+ Ok(o) => {
+ // infinite loop check: the parser must always consume
+ if i.eof_offset() == len {
+ return Err(ErrMode::assert(i, "`repeat` parsers must always consume"));
+ }
+
+ acc.accumulate(o);
+ }
+ }
+ }
+}
+
+fn repeat1_<I, O, C, E, F>(f: &mut F, i: &mut I) -> PResult<C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ F: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ match f.parse_next(i) {
+ Err(e) => Err(e.append(i, ErrorKind::Many)),
+ Ok(o) => {
+ let mut acc = C::initial(None);
+ acc.accumulate(o);
+
+ loop {
+ let start = i.checkpoint();
+ let len = i.eof_offset();
+ match f.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(acc);
+ }
+ Err(e) => return Err(e),
+ Ok(o) => {
+ // infinite loop check: the parser must always consume
+ if i.eof_offset() == len {
+ return Err(ErrMode::assert(i, "`repeat` parsers must always consume"));
+ }
+
+ acc.accumulate(o);
+ }
+ }
+ }
+ }
+ }
+}
+
+/// [`Accumulate`] the output of parser `f` into a container, like `Vec`, until the parser `g`
+/// produces a result.
+///
+/// Returns a tuple of the results of `f` in a `Vec` and the result of `g`.
+///
+/// `f` keeps going so long as `g` produces [`ErrMode::Backtrack`]. To instead chain an error up, see [`cut_err`][crate::combinator::cut_err].
+///
+/// To recognize a series of tokens, [`Accumulate`] into a `()` and then [`Parser::recognize`].
+///
+/// # Example
+///
+/// ```rust
+/// # #[cfg(feature = "std")] {
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::repeat_till0;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, (Vec<&str>, &str)> {
+/// repeat_till0("abc", "end").parse_peek(s)
+/// };
+///
+/// assert_eq!(parser("abcabcend"), Ok(("", (vec!["abc", "abc"], "end"))));
+/// assert_eq!(parser("abc123end"), Err(ErrMode::Backtrack(InputError::new("123end", ErrorKind::Tag))));
+/// assert_eq!(parser("123123end"), Err(ErrMode::Backtrack(InputError::new("123123end", ErrorKind::Tag))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser("abcendefg"), Ok(("efg", (vec!["abc"], "end"))));
+/// # }
+/// ```
+#[doc(alias = "many_till0")]
+pub fn repeat_till0<I, O, C, P, E, F, G>(mut f: F, mut g: G) -> impl Parser<I, (C, P), E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ F: Parser<I, O, E>,
+ G: Parser<I, P, E>,
+ E: ParserError<I>,
+{
+ trace("repeat_till0", move |i: &mut I| {
+ let mut res = C::initial(None);
+ loop {
+ let start = i.checkpoint();
+ let len = i.eof_offset();
+ match g.parse_next(i) {
+ Ok(o) => return Ok((res, o)),
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ match f.parse_next(i) {
+ Err(e) => return Err(e.append(i, ErrorKind::Many)),
+ Ok(o) => {
+ // infinite loop check: the parser must always consume
+ if i.eof_offset() == len {
+ return Err(ErrMode::assert(
+ i,
+ "`repeat` parsers must always consume",
+ ));
+ }
+
+ res.accumulate(o);
+ }
+ }
+ }
+ Err(e) => return Err(e),
+ }
+ }
+ })
+}
+
+/// [`Accumulate`] the output of a parser, 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<I, O, C, O2, E, P, S>(mut parser: P, mut sep: S) -> impl Parser<I, C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ P: Parser<I, O, E>,
+ S: Parser<I, O2, E>,
+ E: ParserError<I>,
+{
+ trace("separated0", move |i: &mut I| {
+ let mut res = C::initial(None);
+
+ let start = i.checkpoint();
+ match parser.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(res);
+ }
+ Err(e) => return Err(e),
+ Ok(o) => {
+ res.accumulate(o);
+ }
+ }
+
+ loop {
+ let start = i.checkpoint();
+ let len = i.eof_offset();
+ match sep.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(res);
+ }
+ Err(e) => return Err(e),
+ Ok(_) => {
+ // infinite loop check: the parser must always consume
+ if i.eof_offset() == len {
+ return Err(ErrMode::assert(i, "sep parsers must always consume"));
+ }
+
+ match parser.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(res);
+ }
+ Err(e) => return Err(e),
+ Ok(o) => {
+ res.accumulate(o);
+ }
+ }
+ }
+ }
+ }
+ })
+}
+
+/// [`Accumulate`] the output of a parser, 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<I, O, C, O2, E, P, S>(mut parser: P, mut sep: S) -> impl Parser<I, C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ P: Parser<I, O, E>,
+ S: Parser<I, O2, E>,
+ E: ParserError<I>,
+{
+ trace("separated1", move |i: &mut I| {
+ let mut res = C::initial(None);
+
+ // Parse the first element
+ match parser.parse_next(i) {
+ Err(e) => return Err(e),
+ Ok(o) => {
+ res.accumulate(o);
+ }
+ }
+
+ loop {
+ let start = i.checkpoint();
+ let len = i.eof_offset();
+ match sep.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(res);
+ }
+ Err(e) => return Err(e),
+ Ok(_) => {
+ // infinite loop check: the parser must always consume
+ if i.eof_offset() == len {
+ return Err(ErrMode::assert(i, "sep parsers must always consume"));
+ }
+
+ match parser.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(res);
+ }
+ Err(e) => return Err(e),
+ Ok(o) => {
+ res.accumulate(o);
+ }
+ }
+ }
+ }
+ }
+ })
+}
+
+/// Alternates between two parsers, merging the results (left associative)
+///
+/// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
+/// [`cut_err`][crate::combinator::cut_err].
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::separated_foldl1;
+/// use winnow::ascii::dec_int;
+///
+/// fn parser(s: &str) -> IResult<&str, i32> {
+/// separated_foldl1(dec_int, "-", |l, _, r| l - r).parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("9-3-5"), Ok(("", 1)));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Slice))));
+/// ```
+pub fn separated_foldl1<I, O, O2, E, P, S, Op>(
+ mut parser: P,
+ mut sep: S,
+ op: Op,
+) -> impl Parser<I, O, E>
+where
+ I: Stream,
+ P: Parser<I, O, E>,
+ S: Parser<I, O2, E>,
+ E: ParserError<I>,
+ Op: Fn(O, O2, O) -> O,
+{
+ trace("separated_foldl1", move |i: &mut I| {
+ let mut ol = parser.parse_next(i)?;
+
+ loop {
+ let start = i.checkpoint();
+ let len = i.eof_offset();
+ match sep.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(ol);
+ }
+ Err(e) => return Err(e),
+ Ok(s) => {
+ // infinite loop check: the parser must always consume
+ if i.eof_offset() == len {
+ return Err(ErrMode::assert(i, "`repeat` parsers must always consume"));
+ }
+
+ match parser.parse_next(i) {
+ Err(ErrMode::Backtrack(_)) => {
+ i.reset(start);
+ return Ok(ol);
+ }
+ Err(e) => return Err(e),
+ Ok(or) => {
+ ol = op(ol, s, or);
+ }
+ }
+ }
+ }
+ }
+ })
+}
+
+/// Alternates between two parsers, merging the results (right associative)
+///
+/// This stops when either parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
+/// [`cut_err`][crate::combinator::cut_err].
+///
+/// # Example
+///
+/// ```
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::separated_foldr1;
+/// use winnow::ascii::dec_uint;
+///
+/// fn parser(s: &str) -> IResult<&str, u32> {
+/// separated_foldr1(dec_uint, "^", |l: u32, _, r: u32| l.pow(r)).parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("2^3^2"), Ok(("", 512)));
+/// assert_eq!(parser("2"), Ok(("", 2)));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// assert_eq!(parser("def|abc"), Err(ErrMode::Backtrack(InputError::new("def|abc", ErrorKind::Slice))));
+/// ```
+#[cfg(feature = "alloc")]
+pub fn separated_foldr1<I, O, O2, E, P, S, Op>(
+ mut parser: P,
+ mut sep: S,
+ op: Op,
+) -> impl Parser<I, O, E>
+where
+ I: Stream,
+ P: Parser<I, O, E>,
+ S: Parser<I, O2, E>,
+ E: ParserError<I>,
+ Op: Fn(O, O2, O) -> O,
+{
+ trace("separated_foldr1", move |i: &mut I| {
+ let ol = parser.parse_next(i)?;
+ let all: crate::lib::std::vec::Vec<(O2, O)> =
+ repeat(0.., (sep.by_ref(), parser.by_ref())).parse_next(i)?;
+ if let Some((s, or)) = all
+ .into_iter()
+ .rev()
+ .reduce(|(sr, or), (sl, ol)| (sl, op(ol, sr, or)))
+ {
+ let merged = op(ol, s, or);
+ Ok(merged)
+ } else {
+ Ok(ol)
+ }
+ })
+}
+
+fn repeat_m_n_<I, O, C, E, F>(min: usize, max: usize, parse: &mut F, input: &mut I) -> PResult<C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ F: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ if min > max {
+ return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many)));
+ }
+
+ let mut res = C::initial(Some(min));
+ for count in 0..max {
+ let start = input.checkpoint();
+ let len = input.eof_offset();
+ match parse.parse_next(input) {
+ Ok(value) => {
+ // infinite loop check: the parser must always consume
+ if input.eof_offset() == len {
+ return Err(ErrMode::assert(
+ input,
+ "`repeat` parsers must always consume",
+ ));
+ }
+
+ res.accumulate(value);
+ }
+ Err(ErrMode::Backtrack(e)) => {
+ if count < min {
+ return Err(ErrMode::Backtrack(e.append(input, ErrorKind::Many)));
+ } else {
+ input.reset(start);
+ return Ok(res);
+ }
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+
+ Ok(res)
+}
+
+fn repeat_n_<I, O, C, E, F>(count: usize, f: &mut F, i: &mut I) -> PResult<C, E>
+where
+ I: Stream,
+ C: Accumulate<O>,
+ F: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ let mut res = C::initial(Some(count));
+
+ for _ in 0..count {
+ match f.parse_next(i) {
+ Ok(o) => {
+ res.accumulate(o);
+ }
+ Err(e) => {
+ return Err(e.append(i, ErrorKind::Many));
+ }
+ }
+ }
+
+ Ok(res)
+}
+
+/// Repeats the embedded parser, filling the given slice with results.
+///
+/// This parser fails if the input runs out before the given slice is full.
+///
+/// # Arguments
+/// * `f` The parser to apply.
+/// * `buf` The slice to fill
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::fill;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, [&str; 2]> {
+/// let mut buf = ["", ""];
+/// let (rest, ()) = fill("abc", &mut buf).parse_peek(s)?;
+/// Ok((rest, buf))
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", ["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
+/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Tag))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser("abcabcabc"), Ok(("abc", ["abc", "abc"])));
+/// ```
+pub fn fill<'a, I, O, E, F>(mut f: F, buf: &'a mut [O]) -> impl Parser<I, (), E> + 'a
+where
+ I: Stream + 'a,
+ F: Parser<I, O, E> + 'a,
+ E: ParserError<I> + 'a,
+{
+ trace("fill", move |i: &mut I| {
+ for elem in buf.iter_mut() {
+ match f.parse_next(i) {
+ Ok(o) => {
+ *elem = o;
+ }
+ Err(e) => {
+ return Err(e.append(i, ErrorKind::Many));
+ }
+ }
+ }
+
+ Ok(())
+ })
+}
+
+/// Repeats the embedded parser `m..=n` times, calling `g` to gather the results
+///
+/// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see
+/// [`cut_err`][crate::combinator::cut_err].
+///
+/// # Arguments
+/// * `m` The minimum number of iterations.
+/// * `n` The maximum number of iterations.
+/// * `f` The parser to apply.
+/// * `init` A function returning the initial value.
+/// * `g` The function that combines a result of `f` with
+/// the current accumulator.
+///
+/// **Warning:** If the parser passed to `fold_repeat` accepts empty inputs
+/// (like `alpha0` or `digit0`), `fold_repeat` will return an error,
+/// to prevent going into an infinite loop.
+///
+/// # Example
+///
+/// Zero or more repetitions:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::fold_repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// fold_repeat(
+/// 0..,
+/// "abc",
+/// Vec::new,
+/// |mut acc: Vec<_>, item| {
+/// acc.push(item);
+/// acc
+/// }
+/// ).parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+/// assert_eq!(parser(""), Ok(("", vec![])));
+/// ```
+///
+/// One or more repetitions:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::fold_repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// fold_repeat(
+/// 1..,
+/// "abc",
+/// Vec::new,
+/// |mut acc: Vec<_>, item| {
+/// acc.push(item);
+/// acc
+/// }
+/// ).parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Many))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Many))));
+/// ```
+///
+/// Arbitrary number of repetitions:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::combinator::fold_repeat;
+/// use winnow::token::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+/// fold_repeat(
+/// 0..=2,
+/// "abc",
+/// Vec::new,
+/// |mut acc: Vec<_>, item| {
+/// acc.push(item);
+/// acc
+/// }
+/// ).parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+/// assert_eq!(parser(""), Ok(("", vec![])));
+/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"])));
+/// ```
+#[doc(alias = "fold_many0")]
+#[doc(alias = "fold_many1")]
+#[doc(alias = "fold_many_m_n")]
+#[inline(always)]
+pub fn fold_repeat<I, O, E, F, G, H, R>(
+ range: impl Into<Range>,
+ mut f: F,
+ mut init: H,
+ mut g: G,
+) -> impl Parser<I, R, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+ G: FnMut(R, O) -> R,
+ H: FnMut() -> R,
+ E: ParserError<I>,
+{
+ let Range {
+ start_inclusive,
+ end_inclusive,
+ } = range.into();
+ trace("fold_repeat", move |i: &mut I| {
+ match (start_inclusive, end_inclusive) {
+ (0, None) => fold_repeat0_(&mut f, &mut init, &mut g, i),
+ (1, None) => fold_repeat1_(&mut f, &mut init, &mut g, i),
+ (start, end) => fold_repeat_m_n_(
+ start,
+ end.unwrap_or(usize::MAX),
+ &mut f,
+ &mut init,
+ &mut g,
+ i,
+ ),
+ }
+ })
+}
+
+fn fold_repeat0_<I, O, E, F, G, H, R>(
+ f: &mut F,
+ init: &mut H,
+ g: &mut G,
+ input: &mut I,
+) -> PResult<R, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+ G: FnMut(R, O) -> R,
+ H: FnMut() -> R,
+ E: ParserError<I>,
+{
+ let mut res = init();
+
+ loop {
+ let start = input.checkpoint();
+ let len = input.eof_offset();
+ match f.parse_next(input) {
+ Ok(o) => {
+ // infinite loop check: the parser must always consume
+ if input.eof_offset() == len {
+ return Err(ErrMode::assert(
+ input,
+ "`repeat` parsers must always consume",
+ ));
+ }
+
+ res = g(res, o);
+ }
+ Err(ErrMode::Backtrack(_)) => {
+ input.reset(start);
+ return Ok(res);
+ }
+ Err(e) => {
+ return Err(e);
+ }
+ }
+ }
+}
+
+fn fold_repeat1_<I, O, E, F, G, H, R>(
+ f: &mut F,
+ init: &mut H,
+ g: &mut G,
+ input: &mut I,
+) -> PResult<R, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+ G: FnMut(R, O) -> R,
+ H: FnMut() -> R,
+ E: ParserError<I>,
+{
+ let init = init();
+ match f.parse_next(input) {
+ Err(ErrMode::Backtrack(_)) => Err(ErrMode::from_error_kind(input, ErrorKind::Many)),
+ Err(e) => Err(e),
+ Ok(o1) => {
+ let mut acc = g(init, o1);
+
+ loop {
+ let start = input.checkpoint();
+ let len = input.eof_offset();
+ match f.parse_next(input) {
+ Err(ErrMode::Backtrack(_)) => {
+ input.reset(start);
+ break;
+ }
+ Err(e) => return Err(e),
+ Ok(o) => {
+ // infinite loop check: the parser must always consume
+ if input.eof_offset() == len {
+ return Err(ErrMode::assert(
+ input,
+ "`repeat` parsers must always consume",
+ ));
+ }
+
+ acc = g(acc, o);
+ }
+ }
+ }
+
+ Ok(acc)
+ }
+ }
+}
+
+fn fold_repeat_m_n_<I, O, E, F, G, H, R>(
+ min: usize,
+ max: usize,
+ parse: &mut F,
+ init: &mut H,
+ fold: &mut G,
+ input: &mut I,
+) -> PResult<R, E>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+ G: FnMut(R, O) -> R,
+ H: FnMut() -> R,
+ E: ParserError<I>,
+{
+ if min > max {
+ return Err(ErrMode::Cut(E::from_error_kind(input, ErrorKind::Many)));
+ }
+
+ let mut acc = init();
+ for count in 0..max {
+ let start = input.checkpoint();
+ let len = input.eof_offset();
+ match parse.parse_next(input) {
+ Ok(value) => {
+ // infinite loop check: the parser must always consume
+ if input.eof_offset() == len {
+ return Err(ErrMode::assert(
+ input,
+ "`repeat` parsers must always consume",
+ ));
+ }
+
+ acc = fold(acc, value);
+ }
+ //FInputXMError: handle failure properly
+ Err(ErrMode::Backtrack(err)) => {
+ if count < min {
+ return Err(ErrMode::Backtrack(err.append(input, ErrorKind::Many)));
+ } else {
+ input.reset(start);
+ break;
+ }
+ }
+ Err(e) => return Err(e),
+ }
+ }
+
+ Ok(acc)
+}
diff --git a/src/combinator/parser.rs b/src/combinator/parser.rs
new file mode 100644
index 0000000..fb11adc
--- /dev/null
+++ b/src/combinator/parser.rs
@@ -0,0 +1,863 @@
+use crate::error::{AddContext, ErrMode, ErrorKind, FromExternalError, ParserError};
+use crate::lib::std::borrow::Borrow;
+use crate::lib::std::ops::Range;
+use crate::stream::StreamIsPartial;
+use crate::stream::{Location, Stream};
+use crate::trace::trace;
+use crate::trace::trace_result;
+use crate::*;
+
+/// Implementation of [`Parser::by_ref`][Parser::by_ref]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct ByRef<'p, P> {
+ p: &'p mut P,
+}
+
+impl<'p, P> ByRef<'p, P> {
+ #[inline(always)]
+ pub(crate) fn new(p: &'p mut P) -> Self {
+ Self { p }
+ }
+}
+
+impl<'p, I, O, E, P> Parser<I, O, E> for ByRef<'p, P>
+where
+ P: Parser<I, O, E>,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+ self.p.parse_next(i)
+ }
+}
+
+/// Implementation of [`Parser::map`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Map<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Fn(O) -> O2,
+{
+ parser: F,
+ map: G,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> Map<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Fn(O) -> O2,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F, map: G) -> Self {
+ Self {
+ parser,
+ map,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O2, E> for Map<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Fn(O) -> O2,
+{
+ #[inline]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+ match self.parser.parse_next(i) {
+ Err(e) => Err(e),
+ Ok(o) => Ok((self.map)(o)),
+ }
+ }
+}
+
+/// Implementation of [`Parser::try_map`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct TryMap<F, G, I, O, O2, E, E2>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> Result<O2, E2>,
+ I: Stream,
+ E: FromExternalError<I, E2>,
+{
+ parser: F,
+ map: G,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+ e2: core::marker::PhantomData<E2>,
+}
+
+impl<F, G, I, O, O2, E, E2> TryMap<F, G, I, O, O2, E, E2>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> Result<O2, E2>,
+ I: Stream,
+ E: FromExternalError<I, E2>,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F, map: G) -> Self {
+ Self {
+ parser,
+ map,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ e2: Default::default(),
+ }
+ }
+}
+
+impl<F, G, I, O, O2, E, E2> Parser<I, O2, E> for TryMap<F, G, I, O, O2, E, E2>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> Result<O2, E2>,
+ I: Stream,
+ E: FromExternalError<I, E2>,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+ let start = input.checkpoint();
+ let o = self.parser.parse_next(input)?;
+ let res = (self.map)(o).map_err(|err| {
+ input.reset(start);
+ ErrMode::from_external_error(input, ErrorKind::Verify, err)
+ });
+ trace_result("verify", &res);
+ res
+ }
+}
+
+/// Implementation of [`Parser::verify_map`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct VerifyMap<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> Option<O2>,
+ I: Stream,
+ E: ParserError<I>,
+{
+ parser: F,
+ map: G,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> VerifyMap<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> Option<O2>,
+ I: Stream,
+ E: ParserError<I>,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F, map: G) -> Self {
+ Self {
+ parser,
+ map,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O2, E> for VerifyMap<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> Option<O2>,
+ I: Stream,
+ E: ParserError<I>,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+ let start = input.checkpoint();
+ let o = self.parser.parse_next(input)?;
+ let res = (self.map)(o).ok_or_else(|| {
+ input.reset(start);
+ ErrMode::from_error_kind(input, ErrorKind::Verify)
+ });
+ trace_result("verify", &res);
+ res
+ }
+}
+
+/// Implementation of [`Parser::and_then`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct AndThen<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Parser<O, O2, E>,
+ O: StreamIsPartial,
+ I: Stream,
+{
+ outer: F,
+ inner: G,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> AndThen<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Parser<O, O2, E>,
+ O: StreamIsPartial,
+ I: Stream,
+{
+ #[inline(always)]
+ pub(crate) fn new(outer: F, inner: G) -> Self {
+ Self {
+ outer,
+ inner,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O2, E> for AndThen<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Parser<O, O2, E>,
+ O: StreamIsPartial,
+ I: Stream,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+ let start = i.checkpoint();
+ let mut o = self.outer.parse_next(i)?;
+ let _ = o.complete();
+ let o2 = self.inner.parse_next(&mut o).map_err(|err| {
+ i.reset(start);
+ err
+ })?;
+ Ok(o2)
+ }
+}
+
+/// Implementation of [`Parser::parse_to`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct ParseTo<P, I, O, O2, E>
+where
+ P: Parser<I, O, E>,
+ I: Stream,
+ O: crate::stream::ParseSlice<O2>,
+ E: ParserError<I>,
+{
+ p: P,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<P, I, O, O2, E> ParseTo<P, I, O, O2, E>
+where
+ P: Parser<I, O, E>,
+ I: Stream,
+ O: crate::stream::ParseSlice<O2>,
+ E: ParserError<I>,
+{
+ #[inline(always)]
+ pub(crate) fn new(p: P) -> Self {
+ Self {
+ p,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<P, I, O, O2, E> Parser<I, O2, E> for ParseTo<P, I, O, O2, E>
+where
+ P: Parser<I, O, E>,
+ I: Stream,
+ O: crate::stream::ParseSlice<O2>,
+ E: ParserError<I>,
+{
+ #[inline]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+ let start = i.checkpoint();
+ let o = self.p.parse_next(i)?;
+ let res = o.parse_slice().ok_or_else(|| {
+ i.reset(start);
+ ErrMode::from_error_kind(i, ErrorKind::Verify)
+ });
+ trace_result("verify", &res);
+ res
+ }
+}
+
+/// Implementation of [`Parser::flat_map`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct FlatMap<F, G, H, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> H,
+ H: Parser<I, O2, E>,
+{
+ f: F,
+ g: G,
+ h: core::marker::PhantomData<H>,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, H, I, O, O2, E> FlatMap<F, G, H, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> H,
+ H: Parser<I, O2, E>,
+{
+ #[inline(always)]
+ pub(crate) fn new(f: F, g: G) -> Self {
+ Self {
+ f,
+ g,
+ h: Default::default(),
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, G, H, I, O, O2, E> Parser<I, O2, E> for FlatMap<F, G, H, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: FnMut(O) -> H,
+ H: Parser<I, O2, E>,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+ let o = self.f.parse_next(i)?;
+ (self.g)(o).parse_next(i)
+ }
+}
+
+/// Implementation of [`Parser::complete_err`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct CompleteErr<F> {
+ f: F,
+}
+
+impl<F> CompleteErr<F> {
+ #[inline(always)]
+ pub(crate) fn new(f: F) -> Self {
+ Self { f }
+ }
+}
+
+impl<F, I, O, E> Parser<I, O, E> for CompleteErr<F>
+where
+ I: Stream,
+ F: Parser<I, O, E>,
+ E: ParserError<I>,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<O, E> {
+ trace("complete_err", |input: &mut I| {
+ match (self.f).parse_next(input) {
+ Err(ErrMode::Incomplete(_)) => {
+ Err(ErrMode::from_error_kind(input, ErrorKind::Complete))
+ }
+ rest => rest,
+ }
+ })
+ .parse_next(input)
+ }
+}
+
+/// Implementation of [`Parser::verify`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Verify<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Fn(&O2) -> bool,
+ I: Stream,
+ O: Borrow<O2>,
+ O2: ?Sized,
+ E: ParserError<I>,
+{
+ parser: F,
+ filter: G,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, G, I, O, O2, E> Verify<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Fn(&O2) -> bool,
+ I: Stream,
+ O: Borrow<O2>,
+ O2: ?Sized,
+ E: ParserError<I>,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F, filter: G) -> Self {
+ Self {
+ parser,
+ filter,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, G, I, O, O2, E> Parser<I, O, E> for Verify<F, G, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ G: Fn(&O2) -> bool,
+ I: Stream,
+ O: Borrow<O2>,
+ O2: ?Sized,
+ E: ParserError<I>,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<O, E> {
+ let start = input.checkpoint();
+ let o = self.parser.parse_next(input)?;
+ let res = (self.filter)(o.borrow()).then_some(o).ok_or_else(|| {
+ input.reset(start);
+ ErrMode::from_error_kind(input, ErrorKind::Verify)
+ });
+ trace_result("verify", &res);
+ res
+ }
+}
+
+/// Implementation of [`Parser::value`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Value<F, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ O2: Clone,
+{
+ parser: F,
+ val: O2,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, O2, E> Value<F, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ O2: Clone,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F, val: O2) -> Self {
+ Self {
+ parser,
+ val,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, O2, E> Parser<I, O2, E> for Value<F, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ O2: Clone,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<O2, E> {
+ (self.parser).parse_next(input).map(|_| self.val.clone())
+ }
+}
+
+/// Implementation of [`Parser::void`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Void<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> Void<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, E> Parser<I, (), E> for Void<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, input: &mut I) -> PResult<(), E> {
+ (self.parser).parse_next(input).map(|_| ())
+ }
+}
+
+/// Implementation of [`Parser::recognize`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Recognize<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> Recognize<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<I, O, E, F> Parser<I, <I as Stream>::Slice, E> for Recognize<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<<I as Stream>::Slice, E> {
+ let checkpoint = input.checkpoint();
+ match (self.parser).parse_next(input) {
+ Ok(_) => {
+ let offset = input.offset_from(&checkpoint);
+ input.reset(checkpoint);
+ let recognized = input.next_slice(offset);
+ Ok(recognized)
+ }
+ Err(e) => Err(e),
+ }
+ }
+}
+
+/// Implementation of [`Parser::with_recognized`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct WithRecognized<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> WithRecognized<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, E> Parser<I, (O, <I as Stream>::Slice), E> for WithRecognized<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<(O, <I as Stream>::Slice), E> {
+ let checkpoint = input.checkpoint();
+ match (self.parser).parse_next(input) {
+ Ok(result) => {
+ let offset = input.offset_from(&checkpoint);
+ input.reset(checkpoint);
+ let recognized = input.next_slice(offset);
+ Ok((result, recognized))
+ }
+ Err(e) => Err(e),
+ }
+ }
+}
+
+/// Implementation of [`Parser::span`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Span<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream + Location,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> Span<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream + Location,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<I, O, E, F> Parser<I, Range<usize>, E> for Span<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream + Location,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<Range<usize>, E> {
+ let start = input.location();
+ self.parser.parse_next(input).map(move |_| {
+ let end = input.location();
+ start..end
+ })
+ }
+}
+
+/// Implementation of [`Parser::with_span`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct WithSpan<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream + Location,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E> WithSpan<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream + Location,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, E> Parser<I, (O, Range<usize>), E> for WithSpan<F, I, O, E>
+where
+ F: Parser<I, O, E>,
+ I: Stream + Location,
+{
+ #[inline]
+ fn parse_next(&mut self, input: &mut I) -> PResult<(O, Range<usize>), E> {
+ let start = input.location();
+ self.parser.parse_next(input).map(move |output| {
+ let end = input.location();
+ (output, (start..end))
+ })
+ }
+}
+
+/// Implementation of [`Parser::output_into`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct OutputInto<F, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ O: Into<O2>,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ o2: core::marker::PhantomData<O2>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, O2, E> OutputInto<F, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ O: Into<O2>,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ o2: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, O2, E> Parser<I, O2, E> for OutputInto<F, I, O, O2, E>
+where
+ F: Parser<I, O, E>,
+ O: Into<O2>,
+{
+ #[inline]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O2, E> {
+ self.parser.parse_next(i).map(|o| o.into())
+ }
+}
+
+/// Implementation of [`Parser::err_into`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct ErrInto<F, I, O, E, E2>
+where
+ F: Parser<I, O, E>,
+ E: Into<E2>,
+{
+ parser: F,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+ e2: core::marker::PhantomData<E2>,
+}
+
+impl<F, I, O, E, E2> ErrInto<F, I, O, E, E2>
+where
+ F: Parser<I, O, E>,
+ E: Into<E2>,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F) -> Self {
+ Self {
+ parser,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ e2: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, E, E2> Parser<I, O, E2> for ErrInto<F, I, O, E, E2>
+where
+ F: Parser<I, O, E>,
+ E: Into<E2>,
+{
+ #[inline]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O, E2> {
+ match self.parser.parse_next(i) {
+ Ok(ok) => Ok(ok),
+ Err(ErrMode::Backtrack(e)) => Err(ErrMode::Backtrack(e.into())),
+ Err(ErrMode::Cut(e)) => Err(ErrMode::Cut(e.into())),
+ Err(ErrMode::Incomplete(e)) => Err(ErrMode::Incomplete(e)),
+ }
+ }
+}
+
+/// Implementation of [`Parser::context`]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub struct Context<F, I, O, E, C>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+ E: AddContext<I, C>,
+ C: Clone + crate::lib::std::fmt::Debug,
+{
+ parser: F,
+ context: C,
+ i: core::marker::PhantomData<I>,
+ o: core::marker::PhantomData<O>,
+ e: core::marker::PhantomData<E>,
+}
+
+impl<F, I, O, E, C> Context<F, I, O, E, C>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+ E: AddContext<I, C>,
+ C: Clone + crate::lib::std::fmt::Debug,
+{
+ #[inline(always)]
+ pub(crate) fn new(parser: F, context: C) -> Self {
+ Self {
+ parser,
+ context,
+ i: Default::default(),
+ o: Default::default(),
+ e: Default::default(),
+ }
+ }
+}
+
+impl<F, I, O, E, C> Parser<I, O, E> for Context<F, I, O, E, C>
+where
+ F: Parser<I, O, E>,
+ I: Stream,
+ E: AddContext<I, C>,
+ C: Clone + crate::lib::std::fmt::Debug,
+{
+ #[inline]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+ #[cfg(feature = "debug")]
+ let name = format!("context={:?}", self.context);
+ #[cfg(not(feature = "debug"))]
+ let name = "context";
+ trace(name, move |i: &mut I| {
+ (self.parser)
+ .parse_next(i)
+ .map_err(|err| err.add_context(i, self.context.clone()))
+ })
+ .parse_next(i)
+ }
+}
diff --git a/src/combinator/sequence.rs b/src/combinator/sequence.rs
new file mode 100644
index 0000000..5cfeb9c
--- /dev/null
+++ b/src/combinator/sequence.rs
@@ -0,0 +1,164 @@
+use crate::error::ParserError;
+use crate::stream::Stream;
+use crate::trace::trace;
+use crate::*;
+
+/// Sequence two parsers, only returning the output from the second.
+///
+/// # Arguments
+/// * `first` The opening parser.
+/// * `second` The second parser to get object.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::combinator::preceded;
+/// use winnow::token::tag;
+///
+/// let mut parser = preceded("abc", "efg");
+///
+/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "efg")));
+/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "efg")));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
+/// ```
+#[doc(alias = "ignore_then")]
+pub fn preceded<I, O1, O2, E: ParserError<I>, F, G>(
+ mut first: F,
+ mut second: G,
+) -> impl Parser<I, O2, E>
+where
+ I: Stream,
+ F: Parser<I, O1, E>,
+ G: Parser<I, O2, E>,
+{
+ trace("preceded", move |input: &mut I| {
+ let _ = first.parse_next(input)?;
+ second.parse_next(input)
+ })
+}
+
+/// Sequence two parsers, only returning the output of the first.
+///
+/// # Arguments
+/// * `first` The first parser to apply.
+/// * `second` The second parser to match an object.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::error::Needed::Size;
+/// use winnow::combinator::terminated;
+/// use winnow::token::tag;
+///
+/// let mut parser = terminated("abc", "efg");
+///
+/// assert_eq!(parser.parse_peek("abcefg"), Ok(("", "abc")));
+/// assert_eq!(parser.parse_peek("abcefghij"), Ok(("hij", "abc")));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
+/// ```
+#[doc(alias = "then_ignore")]
+pub fn terminated<I, O1, O2, E: ParserError<I>, F, G>(
+ mut first: F,
+ mut second: G,
+) -> impl Parser<I, O1, E>
+where
+ I: Stream,
+ F: Parser<I, O1, E>,
+ G: Parser<I, O2, E>,
+{
+ trace("terminated", move |input: &mut I| {
+ let o1 = first.parse_next(input)?;
+ second.parse_next(input).map(|_| o1)
+ })
+}
+
+/// Sequence three parsers, only returning the values of the first and third.
+///
+/// # Arguments
+/// * `first` The first parser to apply.
+/// * `sep` The separator parser to apply.
+/// * `second` The second parser to apply.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::error::Needed::Size;
+/// # use winnow::prelude::*;
+/// use winnow::combinator::separated_pair;
+/// use winnow::token::tag;
+///
+/// let mut parser = separated_pair("abc", "|", "efg");
+///
+/// assert_eq!(parser.parse_peek("abc|efg"), Ok(("", ("abc", "efg"))));
+/// assert_eq!(parser.parse_peek("abc|efghij"), Ok(("hij", ("abc", "efg"))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
+/// ```
+pub fn separated_pair<I, O1, O2, O3, E: ParserError<I>, F, G, H>(
+ mut first: F,
+ mut sep: G,
+ mut second: H,
+) -> impl Parser<I, (O1, O3), E>
+where
+ I: Stream,
+ F: Parser<I, O1, E>,
+ G: Parser<I, O2, E>,
+ H: Parser<I, O3, E>,
+{
+ trace("separated_pair", move |input: &mut I| {
+ let o1 = first.parse_next(input)?;
+ let _ = sep.parse_next(input)?;
+ second.parse_next(input).map(|o2| (o1, o2))
+ })
+}
+
+/// Sequence three parsers, only returning the output of the second.
+///
+/// # Arguments
+/// * `first` The first parser to apply and discard.
+/// * `second` The second parser to apply.
+/// * `third` The third parser to apply and discard.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::error::Needed::Size;
+/// # use winnow::prelude::*;
+/// use winnow::combinator::delimited;
+/// use winnow::token::tag;
+///
+/// let mut parser = delimited("(", "abc", ")");
+///
+/// assert_eq!(parser.parse_peek("(abc)"), Ok(("", "abc")));
+/// assert_eq!(parser.parse_peek("(abc)def"), Ok(("def", "abc")));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// assert_eq!(parser.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Tag))));
+/// ```
+#[doc(alias = "between")]
+#[doc(alias = "padded")]
+pub fn delimited<I, O1, O2, O3, E: ParserError<I>, F, G, H>(
+ mut first: F,
+ mut second: G,
+ mut third: H,
+) -> impl Parser<I, O2, E>
+where
+ I: Stream,
+ F: Parser<I, O1, E>,
+ G: Parser<I, O2, E>,
+ H: Parser<I, O3, E>,
+{
+ trace("delimited", move |input: &mut I| {
+ let _ = first.parse_next(input)?;
+ let o2 = second.parse_next(input)?;
+ third.parse_next(input).map(|_| o2)
+ })
+}
diff --git a/src/combinator/tests.rs b/src/combinator/tests.rs
new file mode 100644
index 0000000..9d2b49d
--- /dev/null
+++ b/src/combinator/tests.rs
@@ -0,0 +1,1292 @@
+use super::*;
+
+use crate::ascii::digit1 as digit;
+use crate::binary::u16;
+use crate::binary::u8;
+use crate::binary::Endianness;
+use crate::error::ErrMode;
+use crate::error::ErrorKind;
+use crate::error::InputError;
+use crate::error::Needed;
+use crate::error::ParserError;
+use crate::stream::Stream;
+use crate::token::take;
+use crate::unpeek;
+use crate::IResult;
+use crate::PResult;
+use crate::Parser;
+use crate::Partial;
+
+#[cfg(feature = "alloc")]
+use crate::lib::std::vec::Vec;
+
+macro_rules! assert_parse(
+ ($left: expr, $right: expr) => {
+ let res: $crate::IResult<_, _, InputError<_>> = $left;
+ assert_eq!(res, $right);
+ };
+);
+
+#[test]
+fn eof_on_slices() {
+ let not_over: &[u8] = &b"Hello, world!"[..];
+ let is_over: &[u8] = &b""[..];
+
+ let res_not_over = eof.parse_peek(not_over);
+ assert_parse!(
+ res_not_over,
+ Err(ErrMode::Backtrack(error_position!(
+ &not_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!(
+ &not_over,
+ ErrorKind::Eof
+ )))
+ );
+
+ let res_over = eof.parse_peek(is_over);
+ assert_parse!(res_over, Ok((is_over, is_over)));
+}
+
+#[test]
+fn rest_on_slices() {
+ let input: &[u8] = &b"Hello, world!"[..];
+ let empty: &[u8] = &b""[..];
+ assert_parse!(rest.parse_peek(input), Ok((empty, input)));
+}
+
+#[test]
+fn rest_on_strs() {
+ let input: &str = "Hello, world!";
+ let empty: &str = "";
+ assert_parse!(rest.parse_peek(input), Ok((empty, input)));
+}
+
+#[test]
+fn rest_len_on_slices() {
+ let input: &[u8] = &b"Hello, world!"[..];
+ assert_parse!(rest_len.parse_peek(input), Ok((input, input.len())));
+}
+
+use crate::lib::std::convert::From;
+impl From<u32> for CustomError {
+ fn from(_: u32) -> Self {
+ CustomError
+ }
+}
+
+impl<I> ParserError<I> for CustomError {
+ fn from_error_kind(_: &I, _: ErrorKind) -> Self {
+ CustomError
+ }
+
+ fn append(self, _: &I, _: ErrorKind) -> Self {
+ CustomError
+ }
+}
+
+struct CustomError;
+#[allow(dead_code)]
+fn custom_error(input: &[u8]) -> IResult<&[u8], &[u8], CustomError> {
+ //fix_error!(input, CustomError<_>, alphanumeric)
+ crate::ascii::alphanumeric1.parse_peek(input)
+}
+
+#[test]
+fn test_parser_flat_map() {
+ let input: &[u8] = &[3, 100, 101, 102, 103, 104][..];
+ assert_parse!(
+ u8.flat_map(take).parse_peek(input),
+ Ok((&[103, 104][..], &[100, 101, 102][..]))
+ );
+}
+
+#[allow(dead_code)]
+fn test_closure_compiles_195(input: &[u8]) -> IResult<&[u8], ()> {
+ u8.flat_map(|num| repeat(num as usize, u16(Endianness::Big)))
+ .parse_peek(input)
+}
+
+#[test]
+fn test_parser_verify_map() {
+ let input: &[u8] = &[50][..];
+ assert_parse!(
+ u8.verify_map(|u| if u < 20 { Some(u) } else { None })
+ .parse_peek(input),
+ Err(ErrMode::Backtrack(InputError::new(
+ &[50][..],
+ ErrorKind::Verify
+ )))
+ );
+ assert_parse!(
+ u8.verify_map(|u| if u > 20 { Some(u) } else { None })
+ .parse_peek(input),
+ Ok((&[][..], 50))
+ );
+}
+
+#[test]
+fn test_parser_map_parser() {
+ let input: &[u8] = &[100, 101, 102, 103, 104][..];
+ assert_parse!(
+ take(4usize).and_then(take(2usize)).parse_peek(input),
+ Ok((&[104][..], &[100, 101][..]))
+ );
+}
+
+#[test]
+#[cfg(feature = "std")]
+fn test_parser_into() {
+ use crate::error::InputError;
+ use crate::token::take;
+
+ let mut parser = take::<_, _, InputError<_>>(3u8).output_into();
+ let result: IResult<&[u8], Vec<u8>> = parser.parse_peek(&b"abcdefg"[..]);
+
+ assert_eq!(result, Ok((&b"defg"[..], vec![97, 98, 99])));
+}
+
+#[test]
+fn opt_test() {
+ fn opt_abcd(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Option<&[u8]>> {
+ opt("abcd").parse_peek(i)
+ }
+
+ let a = &b"abcdef"[..];
+ let b = &b"bcdefg"[..];
+ let c = &b"ab"[..];
+ assert_eq!(
+ opt_abcd(Partial::new(a)),
+ Ok((Partial::new(&b"ef"[..]), Some(&b"abcd"[..])))
+ );
+ assert_eq!(
+ opt_abcd(Partial::new(b)),
+ Ok((Partial::new(&b"bcdefg"[..]), None))
+ );
+ assert_eq!(
+ opt_abcd(Partial::new(c)),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+}
+
+#[test]
+fn peek_test() {
+ fn peek_tag(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ peek("abcd").parse_peek(i)
+ }
+
+ assert_eq!(
+ peek_tag(Partial::new(&b"abcdef"[..])),
+ Ok((Partial::new(&b"abcdef"[..]), &b"abcd"[..]))
+ );
+ assert_eq!(
+ peek_tag(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ peek_tag(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn not_test() {
+ fn not_aaa(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, ()> {
+ not("aaa").parse_peek(i)
+ }
+
+ assert_eq!(
+ not_aaa(Partial::new(&b"aaa"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"aaa"[..]),
+ ErrorKind::Not
+ )))
+ );
+ assert_eq!(
+ not_aaa(Partial::new(&b"aa"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ not_aaa(Partial::new(&b"abcd"[..])),
+ Ok((Partial::new(&b"abcd"[..]), ()))
+ );
+}
+
+#[test]
+fn test_parser_verify() {
+ use crate::token::take;
+
+ fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take(5u8)
+ .verify(|slice: &[u8]| slice[0] == b'a')
+ .parse_peek(i)
+ }
+ assert_eq!(
+ test(Partial::new(&b"bcd"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ test(Partial::new(&b"bcdefg"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"bcdefg"[..]),
+ ErrorKind::Verify
+ )))
+ );
+ assert_eq!(
+ test(Partial::new(&b"abcdefg"[..])),
+ Ok((Partial::new(&b"fg"[..]), &b"abcde"[..]))
+ );
+}
+
+#[test]
+#[allow(unused)]
+fn test_parser_verify_ref() {
+ use crate::token::take;
+
+ let mut parser1 = take(3u8).verify(|s: &[u8]| s == &b"abc"[..]);
+
+ assert_eq!(
+ parser1.parse_peek(&b"abcd"[..]),
+ Ok((&b"d"[..], &b"abc"[..]))
+ );
+ assert_eq!(
+ parser1.parse_peek(&b"defg"[..]),
+ Err(ErrMode::Backtrack(InputError::new(
+ &b"defg"[..],
+ ErrorKind::Verify
+ )))
+ );
+
+ fn parser2(i: &[u8]) -> IResult<&[u8], u32> {
+ crate::binary::be_u32
+ .verify(|val: &u32| *val < 3)
+ .parse_peek(i)
+ }
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn test_parser_verify_alloc() {
+ use crate::token::take;
+ let mut parser1 = take(3u8)
+ .map(|s: &[u8]| s.to_vec())
+ .verify(|s: &[u8]| s == &b"abc"[..]);
+
+ assert_eq!(
+ parser1.parse_peek(&b"abcd"[..]),
+ Ok((&b"d"[..], b"abc".to_vec()))
+ );
+ assert_eq!(
+ parser1.parse_peek(&b"defg"[..]),
+ Err(ErrMode::Backtrack(InputError::new(
+ &b"defg"[..],
+ ErrorKind::Verify
+ )))
+ );
+}
+
+#[test]
+fn fail_test() {
+ let a = "string";
+ let b = "another string";
+
+ assert_eq!(
+ fail::<_, &str, _>.parse_peek(a),
+ Err(ErrMode::Backtrack(InputError::new(a, ErrorKind::Fail)))
+ );
+ assert_eq!(
+ fail::<_, &str, _>.parse_peek(b),
+ Err(ErrMode::Backtrack(InputError::new(b, ErrorKind::Fail)))
+ );
+}
+
+#[test]
+fn complete() {
+ fn err_test(i: &[u8]) -> IResult<&[u8], &[u8]> {
+ let (i, _) = "ijkl".parse_peek(i)?;
+ "mnop".parse_peek(i)
+ }
+ let a = &b"ijklmn"[..];
+
+ let res_a = err_test(a);
+ assert_eq!(
+ res_a,
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"mn"[..],
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn separated_pair_test() {
+ #[allow(clippy::type_complexity)]
+ fn sep_pair_abc_def(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8])> {
+ separated_pair("abc", ",", "def").parse_peek(i)
+ }
+
+ assert_eq!(
+ sep_pair_abc_def(Partial::new(&b"abc,defghijkl"[..])),
+ Ok((Partial::new(&b"ghijkl"[..]), (&b"abc"[..], &b"def"[..])))
+ );
+ assert_eq!(
+ sep_pair_abc_def(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ sep_pair_abc_def(Partial::new(&b"abc,d"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ sep_pair_abc_def(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ sep_pair_abc_def(Partial::new(&b"xxx,def"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx,def"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ sep_pair_abc_def(Partial::new(&b"abc,xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn preceded_test() {
+ fn preceded_abcd_efgh(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ preceded("abcd", "efgh").parse_peek(i)
+ }
+
+ assert_eq!(
+ preceded_abcd_efgh(Partial::new(&b"abcdefghijkl"[..])),
+ Ok((Partial::new(&b"ijkl"[..]), &b"efgh"[..]))
+ );
+ assert_eq!(
+ preceded_abcd_efgh(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ preceded_abcd_efgh(Partial::new(&b"abcde"[..])),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_eq!(
+ preceded_abcd_efgh(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ preceded_abcd_efgh(Partial::new(&b"xxxxdef"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxxdef"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ preceded_abcd_efgh(Partial::new(&b"abcdxxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn terminated_test() {
+ fn terminated_abcd_efgh(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ terminated("abcd", "efgh").parse_peek(i)
+ }
+
+ assert_eq!(
+ terminated_abcd_efgh(Partial::new(&b"abcdefghijkl"[..])),
+ Ok((Partial::new(&b"ijkl"[..]), &b"abcd"[..]))
+ );
+ assert_eq!(
+ terminated_abcd_efgh(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ terminated_abcd_efgh(Partial::new(&b"abcde"[..])),
+ Err(ErrMode::Incomplete(Needed::new(3)))
+ );
+ assert_eq!(
+ terminated_abcd_efgh(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ terminated_abcd_efgh(Partial::new(&b"xxxxdef"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxxdef"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ terminated_abcd_efgh(Partial::new(&b"abcdxxxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn delimited_test() {
+ fn delimited_abc_def_ghi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ delimited("abc", "def", "ghi").parse_peek(i)
+ }
+
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"abcdefghijkl"[..])),
+ Ok((Partial::new(&b"jkl"[..]), &b"def"[..]))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"abcde"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"abcdefgh"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"xxxdefghi"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxdefghi"[..]),
+ ErrorKind::Tag
+ ),))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"abcxxxghi"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxghi"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ delimited_abc_def_ghi(Partial::new(&b"abcdefxxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[cfg(feature = "alloc")]
+#[test]
+fn alt_test() {
+ #[cfg(feature = "alloc")]
+ use crate::{
+ error::ParserError,
+ lib::std::{
+ fmt::Debug,
+ string::{String, ToString},
+ },
+ };
+
+ #[cfg(feature = "alloc")]
+ #[derive(Debug, Clone, Eq, PartialEq)]
+ pub struct ErrorStr(String);
+
+ #[cfg(feature = "alloc")]
+ impl From<u32> for ErrorStr {
+ fn from(i: u32) -> Self {
+ ErrorStr(format!("custom error code: {}", i))
+ }
+ }
+
+ #[cfg(feature = "alloc")]
+ impl<'a> From<&'a str> for ErrorStr {
+ fn from(i: &'a str) -> Self {
+ ErrorStr(format!("custom error message: {}", i))
+ }
+ }
+
+ #[cfg(feature = "alloc")]
+ impl<I: Debug> ParserError<I> for ErrorStr {
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
+ ErrorStr(format!("custom error message: ({:?}, {:?})", input, kind))
+ }
+
+ fn append(self, input: &I, kind: ErrorKind) -> Self {
+ ErrorStr(format!(
+ "custom error message: ({:?}, {:?}) - {:?}",
+ input, kind, self
+ ))
+ }
+ }
+
+ fn work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
+ Ok(input.peek_finish())
+ }
+
+ #[allow(unused_variables)]
+ fn dont_work(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
+ Err(ErrMode::Backtrack(ErrorStr("abcd".to_string())))
+ }
+
+ fn work2(input: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
+ Ok((input, &b""[..]))
+ }
+
+ fn alt1(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
+ alt((unpeek(dont_work), unpeek(dont_work))).parse_peek(i)
+ }
+ fn alt2(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
+ alt((unpeek(dont_work), unpeek(work))).parse_peek(i)
+ }
+ fn alt3(i: &[u8]) -> IResult<&[u8], &[u8], ErrorStr> {
+ alt((
+ unpeek(dont_work),
+ unpeek(dont_work),
+ unpeek(work2),
+ unpeek(dont_work),
+ ))
+ .parse_peek(i)
+ }
+ //named!(alt1, alt!(dont_work | dont_work));
+ //named!(alt2, alt!(dont_work | work));
+ //named!(alt3, alt!(dont_work | dont_work | work2 | dont_work));
+
+ let a = &b"abcd"[..];
+ assert_eq!(
+ alt1(a),
+ Err(ErrMode::Backtrack(error_node_position!(
+ &a,
+ ErrorKind::Alt,
+ ErrorStr("abcd".to_string())
+ )))
+ );
+ assert_eq!(alt2(a), Ok((&b""[..], a)));
+ assert_eq!(alt3(a), Ok((a, &b""[..])));
+
+ fn alt4(i: &[u8]) -> IResult<&[u8], &[u8]> {
+ alt(("abcd", "efgh")).parse_peek(i)
+ }
+ let b = &b"efgh"[..];
+ assert_eq!(alt4(a), Ok((&b""[..], a)));
+ assert_eq!(alt4(b), Ok((&b""[..], b)));
+}
+
+#[test]
+fn alt_incomplete() {
+ fn alt1(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ alt(("a", "bc", "def")).parse_peek(i)
+ }
+
+ let a = &b""[..];
+ assert_eq!(
+ alt1(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ let a = &b"b"[..];
+ assert_eq!(
+ alt1(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ let a = &b"bcd"[..];
+ assert_eq!(
+ alt1(Partial::new(a)),
+ Ok((Partial::new(&b"d"[..]), &b"bc"[..]))
+ );
+ let a = &b"cde"[..];
+ assert_eq!(
+ alt1(Partial::new(a)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(a),
+ ErrorKind::Tag
+ )))
+ );
+ let a = &b"de"[..];
+ assert_eq!(
+ alt1(Partial::new(a)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ let a = &b"defg"[..];
+ assert_eq!(
+ alt1(Partial::new(a)),
+ Ok((Partial::new(&b"g"[..]), &b"def"[..]))
+ );
+}
+
+#[test]
+fn alt_array() {
+ fn alt1<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8]> {
+ alt(["a", "bc", "def"]).parse_next(i)
+ }
+
+ let i = &b"a"[..];
+ assert_eq!(alt1.parse_peek(i), Ok((&b""[..], (&b"a"[..]))));
+
+ let i = &b"bc"[..];
+ assert_eq!(alt1.parse_peek(i), Ok((&b""[..], (&b"bc"[..]))));
+
+ let i = &b"defg"[..];
+ assert_eq!(alt1.parse_peek(i), Ok((&b"g"[..], (&b"def"[..]))));
+
+ let i = &b"z"[..];
+ assert_eq!(
+ alt1.parse_peek(i),
+ Err(ErrMode::Backtrack(error_position!(&i, ErrorKind::Tag)))
+ );
+}
+
+#[test]
+fn permutation_test() {
+ #[allow(clippy::type_complexity)]
+ fn perm(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (&[u8], &[u8], &[u8])> {
+ permutation(("abcd", "efg", "hi")).parse_peek(i)
+ }
+
+ let expected = (&b"abcd"[..], &b"efg"[..], &b"hi"[..]);
+
+ let a = &b"abcdefghijk"[..];
+ assert_eq!(
+ perm(Partial::new(a)),
+ Ok((Partial::new(&b"jk"[..]), expected))
+ );
+ let b = &b"efgabcdhijk"[..];
+ assert_eq!(
+ perm(Partial::new(b)),
+ Ok((Partial::new(&b"jk"[..]), expected))
+ );
+ let c = &b"hiefgabcdjk"[..];
+ assert_eq!(
+ perm(Partial::new(c)),
+ Ok((Partial::new(&b"jk"[..]), expected))
+ );
+
+ let d = &b"efgxyzabcdefghi"[..];
+ assert_eq!(
+ perm(Partial::new(d)),
+ Err(ErrMode::Backtrack(error_node_position!(
+ &Partial::new(&b"efgxyzabcdefghi"[..]),
+ ErrorKind::Alt,
+ error_position!(&Partial::new(&b"xyzabcdefghi"[..]), ErrorKind::Tag)
+ )))
+ );
+
+ let e = &b"efgabc"[..];
+ assert_eq!(
+ perm(Partial::new(e)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn separated0_test() {
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ separated0("abcd", ",").parse_peek(i)
+ }
+ fn multi_empty(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ separated0("", ",").parse_peek(i)
+ }
+ fn multi_longsep(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ separated0("abcd", "..").parse_peek(i)
+ }
+
+ let a = &b"abcdef"[..];
+ let b = &b"abcd,abcdef"[..];
+ let c = &b"azerty"[..];
+ let d = &b",,abc"[..];
+ let e = &b"abcd,abcd,ef"[..];
+ let f = &b"abc"[..];
+ let g = &b"abcd."[..];
+ let h = &b"abcd,abc"[..];
+
+ let res1 = vec![&b"abcd"[..]];
+ assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1)));
+ let res2 = vec![&b"abcd"[..], &b"abcd"[..]];
+ assert_eq!(multi(Partial::new(b)), Ok((Partial::new(&b"ef"[..]), res2)));
+ assert_eq!(
+ multi(Partial::new(c)),
+ Ok((Partial::new(&b"azerty"[..]), Vec::new()))
+ );
+ let res3 = vec![&b""[..], &b""[..], &b""[..]];
+ assert_eq!(
+ multi_empty(Partial::new(d)),
+ Ok((Partial::new(&b"abc"[..]), res3))
+ );
+ let res4 = vec![&b"abcd"[..], &b"abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(e)),
+ Ok((Partial::new(&b",ef"[..]), res4))
+ );
+
+ assert_eq!(
+ multi(Partial::new(f)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ multi_longsep(Partial::new(g)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ multi(Partial::new(h)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+#[cfg_attr(debug_assertions, should_panic)]
+fn separated0_empty_sep_test() {
+ fn empty_sep(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ separated0("abc", "").parse_peek(i)
+ }
+
+ let i = &b"abcabc"[..];
+
+ let i_err_pos = &i[3..];
+ assert_eq!(
+ empty_sep(Partial::new(i)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(i_err_pos),
+ ErrorKind::Assert
+ )))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn separated1_test() {
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ separated1("abcd", ",").parse_peek(i)
+ }
+ fn multi_longsep(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ separated1("abcd", "..").parse_peek(i)
+ }
+
+ let a = &b"abcdef"[..];
+ let b = &b"abcd,abcdef"[..];
+ let c = &b"azerty"[..];
+ let d = &b"abcd,abcd,ef"[..];
+
+ let f = &b"abc"[..];
+ let g = &b"abcd."[..];
+ let h = &b"abcd,abc"[..];
+
+ let res1 = vec![&b"abcd"[..]];
+ assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1)));
+ let res2 = vec![&b"abcd"[..], &b"abcd"[..]];
+ assert_eq!(multi(Partial::new(b)), Ok((Partial::new(&b"ef"[..]), res2)));
+ assert_eq!(
+ multi(Partial::new(c)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(c),
+ ErrorKind::Tag
+ )))
+ );
+ let res3 = vec![&b"abcd"[..], &b"abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(d)),
+ Ok((Partial::new(&b",ef"[..]), res3))
+ );
+
+ assert_eq!(
+ multi(Partial::new(f)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ multi_longsep(Partial::new(g)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ multi(Partial::new(h)),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn repeat0_test() {
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ repeat(0.., "abcd").parse_peek(i)
+ }
+
+ assert_eq!(
+ multi(Partial::new(&b"abcdef"[..])),
+ Ok((Partial::new(&b"ef"[..]), vec![&b"abcd"[..]]))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"abcdabcdefgh"[..])),
+ Ok((Partial::new(&b"efgh"[..]), vec![&b"abcd"[..], &b"abcd"[..]]))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"azerty"[..])),
+ Ok((Partial::new(&b"azerty"[..]), Vec::new()))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"abcdab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"abcd"[..])),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+ assert_eq!(
+ multi(Partial::new(&b""[..])),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+#[cfg_attr(debug_assertions, should_panic)]
+fn repeat0_empty_test() {
+ fn multi_empty(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ repeat(0.., "").parse_peek(i)
+ }
+
+ assert_eq!(
+ multi_empty(Partial::new(&b"abcdef"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"abcdef"[..]),
+ ErrorKind::Assert
+ )))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn repeat1_test() {
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ repeat(1.., "abcd").parse_peek(i)
+ }
+
+ let a = &b"abcdef"[..];
+ let b = &b"abcdabcdefgh"[..];
+ let c = &b"azerty"[..];
+ let d = &b"abcdab"[..];
+
+ let res1 = vec![&b"abcd"[..]];
+ assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1)));
+ let res2 = vec![&b"abcd"[..], &b"abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(b)),
+ Ok((Partial::new(&b"efgh"[..]), res2))
+ );
+ assert_eq!(
+ multi(Partial::new(c)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(c),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ multi(Partial::new(d)),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn repeat_till_test() {
+ #[allow(clippy::type_complexity)]
+ fn multi(i: &[u8]) -> IResult<&[u8], (Vec<&[u8]>, &[u8])> {
+ repeat_till0("abcd", "efgh").parse_peek(i)
+ }
+
+ let a = b"abcdabcdefghabcd";
+ let b = b"efghabcd";
+ let c = b"azerty";
+
+ let res_a = (vec![&b"abcd"[..], &b"abcd"[..]], &b"efgh"[..]);
+ let res_b: (Vec<&[u8]>, &[u8]) = (Vec::new(), &b"efgh"[..]);
+ assert_eq!(multi(&a[..]), Ok((&b"abcd"[..], res_a)));
+ assert_eq!(multi(&b[..]), Ok((&b"abcd"[..], res_b)));
+ assert_eq!(
+ multi(&c[..]),
+ Err(ErrMode::Backtrack(error_node_position!(
+ &&c[..],
+ ErrorKind::Many,
+ error_position!(&&c[..], ErrorKind::Tag)
+ )))
+ );
+}
+
+#[test]
+#[cfg(feature = "std")]
+fn infinite_many() {
+ fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
+ println!("input: {:?}", input);
+ Err(ErrMode::Backtrack(error_position!(&input, ErrorKind::Tag)))
+ }
+
+ // should not go into an infinite loop
+ fn multi0(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+ repeat(0.., unpeek(tst)).parse_peek(i)
+ }
+ let a = &b"abcdef"[..];
+ assert_eq!(multi0(a), Ok((a, Vec::new())));
+
+ fn multi1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+ repeat(1.., unpeek(tst)).parse_peek(i)
+ }
+ let a = &b"abcdef"[..];
+ assert_eq!(
+ multi1(a),
+ Err(ErrMode::Backtrack(error_position!(&a, ErrorKind::Tag)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn repeat_test() {
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ repeat(2..=4, "Abcd").parse_peek(i)
+ }
+
+ let a = &b"Abcdef"[..];
+ let b = &b"AbcdAbcdefgh"[..];
+ let c = &b"AbcdAbcdAbcdAbcdefgh"[..];
+ let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..];
+ let e = &b"AbcdAb"[..];
+
+ assert_eq!(
+ multi(Partial::new(a)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"ef"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(b)),
+ Ok((Partial::new(&b"efgh"[..]), res1))
+ );
+ let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(c)),
+ Ok((Partial::new(&b"efgh"[..]), res2))
+ );
+ let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(d)),
+ Ok((Partial::new(&b"Abcdefgh"[..]), res3))
+ );
+ assert_eq!(
+ multi(Partial::new(e)),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn count_test() {
+ const TIMES: usize = 2;
+ fn cnt_2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ repeat(TIMES, "abc").parse_peek(i)
+ }
+
+ assert_eq!(
+ cnt_2(Partial::new(&b"abcabcabcdef"[..])),
+ Ok((Partial::new(&b"abcdef"[..]), vec![&b"abc"[..], &b"abc"[..]]))
+ );
+ assert_eq!(
+ cnt_2(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ cnt_2(Partial::new(&b"abcab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ cnt_2(Partial::new(&b"xxx"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxx"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ cnt_2(Partial::new(&b"xxxabcabcdef"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxabcabcdef"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ cnt_2(Partial::new(&b"abcxxxabcdef"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"xxxabcdef"[..]),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn count_zero() {
+ const TIMES: usize = 0;
+ fn counter_2(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+ repeat(TIMES, "abc").parse_peek(i)
+ }
+
+ let done = &b"abcabcabcdef"[..];
+ let parsed_done = Vec::new();
+ let rest = done;
+ let incomplete_1 = &b"ab"[..];
+ let parsed_incompl_1 = Vec::new();
+ let incomplete_2 = &b"abcab"[..];
+ let parsed_incompl_2 = Vec::new();
+ let error = &b"xxx"[..];
+ let error_remain = &b"xxx"[..];
+ let parsed_err = Vec::new();
+ let error_1 = &b"xxxabcabcdef"[..];
+ let parsed_err_1 = Vec::new();
+ let error_1_remain = &b"xxxabcabcdef"[..];
+ let error_2 = &b"abcxxxabcdef"[..];
+ let parsed_err_2 = Vec::new();
+ let error_2_remain = &b"abcxxxabcdef"[..];
+
+ assert_eq!(counter_2(done), Ok((rest, parsed_done)));
+ assert_eq!(
+ counter_2(incomplete_1),
+ Ok((incomplete_1, parsed_incompl_1))
+ );
+ assert_eq!(
+ counter_2(incomplete_2),
+ Ok((incomplete_2, parsed_incompl_2))
+ );
+ assert_eq!(counter_2(error), Ok((error_remain, parsed_err)));
+ assert_eq!(counter_2(error_1), Ok((error_1_remain, parsed_err_1)));
+ assert_eq!(counter_2(error_2), Ok((error_2_remain, parsed_err_2)));
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct NilError;
+
+impl<I> From<(I, ErrorKind)> for NilError {
+ fn from(_: (I, ErrorKind)) -> Self {
+ NilError
+ }
+}
+
+impl<I> ParserError<I> for NilError {
+ fn from_error_kind(_: &I, _: ErrorKind) -> NilError {
+ NilError
+ }
+ fn append(self, _: &I, _: ErrorKind) -> NilError {
+ NilError
+ }
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn fold_repeat0_test() {
+ fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> {
+ acc.push(item);
+ acc
+ }
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ fold_repeat(0.., "abcd", Vec::new, fold_into_vec).parse_peek(i)
+ }
+
+ assert_eq!(
+ multi(Partial::new(&b"abcdef"[..])),
+ Ok((Partial::new(&b"ef"[..]), vec![&b"abcd"[..]]))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"abcdabcdefgh"[..])),
+ Ok((Partial::new(&b"efgh"[..]), vec![&b"abcd"[..], &b"abcd"[..]]))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"azerty"[..])),
+ Ok((Partial::new(&b"azerty"[..]), Vec::new()))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"abcdab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ multi(Partial::new(&b"abcd"[..])),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+ assert_eq!(
+ multi(Partial::new(&b""[..])),
+ Err(ErrMode::Incomplete(Needed::new(4)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+#[cfg_attr(debug_assertions, should_panic)]
+fn fold_repeat0_empty_test() {
+ fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> {
+ acc.push(item);
+ acc
+ }
+ fn multi_empty(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ fold_repeat(0.., "", Vec::new, fold_into_vec).parse_peek(i)
+ }
+
+ assert_eq!(
+ multi_empty(Partial::new(&b"abcdef"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"abcdef"[..]),
+ ErrorKind::Assert
+ )))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn fold_repeat1_test() {
+ fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> {
+ acc.push(item);
+ acc
+ }
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ fold_repeat(1.., "abcd", Vec::new, fold_into_vec).parse_peek(i)
+ }
+
+ let a = &b"abcdef"[..];
+ let b = &b"abcdabcdefgh"[..];
+ let c = &b"azerty"[..];
+ let d = &b"abcdab"[..];
+
+ let res1 = vec![&b"abcd"[..]];
+ assert_eq!(multi(Partial::new(a)), Ok((Partial::new(&b"ef"[..]), res1)));
+ let res2 = vec![&b"abcd"[..], &b"abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(b)),
+ Ok((Partial::new(&b"efgh"[..]), res2))
+ );
+ assert_eq!(
+ multi(Partial::new(c)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(c),
+ ErrorKind::Many
+ )))
+ );
+ assert_eq!(
+ multi(Partial::new(d)),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn fold_repeat_test() {
+ fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> {
+ acc.push(item);
+ acc
+ }
+ fn multi(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, Vec<&[u8]>> {
+ fold_repeat(2..=4, "Abcd", Vec::new, fold_into_vec).parse_peek(i)
+ }
+
+ let a = &b"Abcdef"[..];
+ let b = &b"AbcdAbcdefgh"[..];
+ let c = &b"AbcdAbcdAbcdAbcdefgh"[..];
+ let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..];
+ let e = &b"AbcdAb"[..];
+
+ assert_eq!(
+ multi(Partial::new(a)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"ef"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(b)),
+ Ok((Partial::new(&b"efgh"[..]), res1))
+ );
+ let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(c)),
+ Ok((Partial::new(&b"efgh"[..]), res2))
+ );
+ let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+ assert_eq!(
+ multi(Partial::new(d)),
+ Ok((Partial::new(&b"Abcdefgh"[..]), res3))
+ );
+ assert_eq!(
+ multi(Partial::new(e)),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+}
+
+#[test]
+fn repeat0_count_test() {
+ fn count0_nums(i: &[u8]) -> IResult<&[u8], usize> {
+ repeat(0.., (digit, ",")).parse_peek(i)
+ }
+
+ assert_eq!(count0_nums(&b"123,junk"[..]), Ok((&b"junk"[..], 1)));
+
+ assert_eq!(count0_nums(&b"123,45,junk"[..]), Ok((&b"junk"[..], 2)));
+
+ assert_eq!(
+ count0_nums(&b"1,2,3,4,5,6,7,8,9,0,junk"[..]),
+ Ok((&b"junk"[..], 10))
+ );
+
+ assert_eq!(count0_nums(&b"hello"[..]), Ok((&b"hello"[..], 0)));
+}
+
+#[test]
+fn repeat1_count_test() {
+ fn count1_nums(i: &[u8]) -> IResult<&[u8], usize> {
+ repeat(1.., (digit, ",")).parse_peek(i)
+ }
+
+ assert_eq!(count1_nums(&b"123,45,junk"[..]), Ok((&b"junk"[..], 2)));
+
+ assert_eq!(
+ count1_nums(&b"1,2,3,4,5,6,7,8,9,0,junk"[..]),
+ Ok((&b"junk"[..], 10))
+ );
+
+ assert_eq!(
+ count1_nums(&b"hello"[..]),
+ Err(ErrMode::Backtrack(error_position!(
+ &&b"hello"[..],
+ ErrorKind::Slice
+ )))
+ );
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..449bebc
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,1382 @@
+//! # 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<E>)`][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<I, O, E = InputError<I>> = PResult<(I, O), E>;
+
+/// Holds the result of [`Parser`]
+///
+/// - `Ok(O)` is the parsed value
+/// - [`Err(ErrMode<E>)`][ErrMode] is the error along with how to respond to it
+///
+/// By default, the error type (`E`) is [`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<O, E = ContextError> = Result<O, ErrMode<E>>;
+
+/// Contains information on needed data if a parser returned `Incomplete`
+///
+/// **Note:** This is only possible for `Stream` that are [partial][`crate::stream::StreamIsPartial`],
+/// like [`Partial`][crate::Partial].
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub enum Needed {
+ /// Needs more data, but we do not know how much
+ Unknown,
+ /// Contains the required data size in bytes
+ Size(NonZeroUsize),
+}
+
+impl Needed {
+ /// Creates `Needed` instance, returns `Needed::Unknown` if the argument is zero
+ pub fn new(s: usize) -> Self {
+ match NonZeroUsize::new(s) {
+ Some(sz) => Needed::Size(sz),
+ None => Needed::Unknown,
+ }
+ }
+
+ /// Indicates if we know how many bytes we need
+ pub fn is_known(&self) -> bool {
+ *self != Needed::Unknown
+ }
+
+ /// Maps a `Needed` to `Needed` by applying a function to a contained `Size` value.
+ #[inline]
+ pub fn map<F: Fn(NonZeroUsize) -> usize>(self, f: F) -> Needed {
+ match self {
+ Needed::Unknown => Needed::Unknown,
+ Needed::Size(n) => Needed::new(f(n)),
+ }
+ }
+}
+
+/// Add parse error state to [`ParserError`]s
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+pub enum ErrMode<E> {
+ /// There was not enough data to determine the appropriate action
+ ///
+ /// More data needs to be buffered before retrying the parse.
+ ///
+ /// This must only be set when the [`Stream`][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<E> ErrMode<E> {
+ /// Tests if the result is Incomplete
+ #[inline]
+ pub fn is_incomplete(&self) -> bool {
+ matches!(self, ErrMode::Incomplete(_))
+ }
+
+ /// Prevent backtracking, bubbling the error up to the top
+ pub fn cut(self) -> Self {
+ match self {
+ ErrMode::Backtrack(e) => ErrMode::Cut(e),
+ rest => rest,
+ }
+ }
+
+ /// Enable backtracking support
+ pub fn backtrack(self) -> Self {
+ match self {
+ ErrMode::Cut(e) => ErrMode::Backtrack(e),
+ rest => rest,
+ }
+ }
+
+ /// Applies the given function to the inner error
+ pub fn map<E2, F>(self, f: F) -> ErrMode<E2>
+ where
+ F: FnOnce(E) -> E2,
+ {
+ match self {
+ ErrMode::Incomplete(n) => ErrMode::Incomplete(n),
+ ErrMode::Cut(t) => ErrMode::Cut(f(t)),
+ ErrMode::Backtrack(t) => ErrMode::Backtrack(f(t)),
+ }
+ }
+
+ /// Automatically converts between errors if the underlying type supports it
+ pub fn convert<F>(self) -> ErrMode<F>
+ where
+ E: ErrorConvert<F>,
+ {
+ self.map(ErrorConvert::convert)
+ }
+
+ /// Unwrap the mode, returning the underlying error
+ ///
+ /// Returns `None` for [`ErrMode::Incomplete`]
+ #[cfg_attr(debug_assertions, track_caller)]
+ #[inline(always)]
+ pub fn into_inner(self) -> Option<E> {
+ match self {
+ ErrMode::Backtrack(e) | ErrMode::Cut(e) => Some(e),
+ ErrMode::Incomplete(_) => None,
+ }
+ }
+}
+
+impl<I, E: ParserError<I>> ParserError<I> for ErrMode<E> {
+ #[inline(always)]
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
+ ErrMode::Backtrack(E::from_error_kind(input, kind))
+ }
+
+ #[cfg_attr(debug_assertions, track_caller)]
+ #[inline(always)]
+ fn assert(input: &I, message: &'static str) -> Self
+ where
+ I: crate::lib::std::fmt::Debug,
+ {
+ ErrMode::Backtrack(E::assert(input, message))
+ }
+
+ #[inline]
+ fn append(self, input: &I, kind: ErrorKind) -> Self {
+ match self {
+ ErrMode::Backtrack(e) => ErrMode::Backtrack(e.append(input, kind)),
+ e => e,
+ }
+ }
+
+ fn or(self, other: Self) -> Self {
+ match (self, other) {
+ (ErrMode::Backtrack(e), ErrMode::Backtrack(o)) => ErrMode::Backtrack(e.or(o)),
+ (ErrMode::Incomplete(e), _) | (_, ErrMode::Incomplete(e)) => ErrMode::Incomplete(e),
+ (ErrMode::Cut(e), _) | (_, ErrMode::Cut(e)) => ErrMode::Cut(e),
+ }
+ }
+}
+
+impl<I, EXT, E> FromExternalError<I, EXT> for ErrMode<E>
+where
+ E: FromExternalError<I, EXT>,
+{
+ #[inline(always)]
+ fn from_external_error(input: &I, kind: ErrorKind, e: EXT) -> Self {
+ ErrMode::Backtrack(E::from_external_error(input, kind, e))
+ }
+}
+
+impl<I, C, E: AddContext<I, C>> AddContext<I, C> for ErrMode<E> {
+ #[inline(always)]
+ fn add_context(self, input: &I, ctx: C) -> Self {
+ self.map(|err| err.add_context(input, ctx))
+ }
+}
+
+impl<T: Clone> ErrMode<InputError<T>> {
+ /// Maps `ErrMode<InputError<T>>` to `ErrMode<InputError<U>>` with the given `F: T -> U`
+ pub fn map_input<U: Clone, F>(self, f: F) -> ErrMode<InputError<U>>
+ where
+ F: FnOnce(T) -> U,
+ {
+ match self {
+ ErrMode::Incomplete(n) => ErrMode::Incomplete(n),
+ ErrMode::Cut(InputError { input, kind }) => ErrMode::Cut(InputError {
+ input: f(input),
+ kind,
+ }),
+ ErrMode::Backtrack(InputError { input, kind }) => ErrMode::Backtrack(InputError {
+ input: f(input),
+ kind,
+ }),
+ }
+ }
+}
+
+impl<E: Eq> Eq for ErrMode<E> {}
+
+impl<E> fmt::Display for ErrMode<E>
+where
+ E: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ErrMode::Incomplete(Needed::Size(u)) => write!(f, "Parsing requires {} bytes/chars", u),
+ ErrMode::Incomplete(Needed::Unknown) => write!(f, "Parsing requires more data"),
+ ErrMode::Cut(c) => write!(f, "Parsing Failure: {:?}", c),
+ ErrMode::Backtrack(c) => write!(f, "Parsing Error: {:?}", c),
+ }
+ }
+}
+
+/// The basic [`Parser`] trait for errors
+///
+/// It provides methods to create an error from some combinators,
+/// and combine existing errors in combinators like `alt`.
+pub trait ParserError<I>: Sized {
+ /// Creates an error from the input position and an [`ErrorKind`]
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self;
+
+ /// Process a parser assertion
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn assert(input: &I, _message: &'static str) -> Self
+ where
+ I: crate::lib::std::fmt::Debug,
+ {
+ #[cfg(debug_assertions)]
+ panic!("assert `{}` failed at {:#?}", _message, input);
+ #[cfg(not(debug_assertions))]
+ Self::from_error_kind(input, ErrorKind::Assert)
+ }
+
+ /// Like [`ParserError::from_error_kind`] but merges it with the existing error.
+ ///
+ /// This is useful when backtracking through a parse tree, accumulating error context on the
+ /// way.
+ fn append(self, input: &I, kind: ErrorKind) -> Self;
+
+ /// Combines errors from two different parse branches.
+ ///
+ /// For example, this would be used by [`alt`][crate::combinator::alt] to report the error from
+ /// each case.
+ #[inline]
+ fn or(self, other: Self) -> Self {
+ other
+ }
+}
+
+/// Used by [`Parser::context`] to add custom data to error while backtracking
+///
+/// May be implemented multiple times for different kinds of context.
+pub trait AddContext<I, C = &'static str>: Sized {
+ /// Append to an existing error custom data
+ ///
+ /// This is used mainly by [`Parser::context`], to add user friendly information
+ /// to errors when backtracking through a parse tree
+ #[inline]
+ fn add_context(self, _input: &I, _ctx: C) -> Self {
+ self
+ }
+}
+
+/// Create a new error with an external error, from [`std::str::FromStr`]
+///
+/// This trait is required by the [`Parser::try_map`] combinator.
+pub trait FromExternalError<I, E> {
+ /// Like [`ParserError::from_error_kind`] but also include an external error.
+ fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self;
+}
+
+/// Equivalent of `From` implementation to avoid orphan rules in bits parsers
+pub trait ErrorConvert<E> {
+ /// Transform to another error type
+ fn convert(self) -> E;
+}
+
+/// Capture input on error
+///
+/// This is useful for testing of generic parsers to ensure the error happens at the right
+/// location.
+///
+/// **Note:** [context][Parser::context] and inner errors (like from [`Parser::try_map`]) will be
+/// dropped.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct InputError<I: Clone> {
+ /// The input stream, pointing to the location where the error occurred
+ pub input: I,
+ /// A rudimentary error kind
+ pub kind: ErrorKind,
+}
+
+impl<I: Clone> InputError<I> {
+ /// Creates a new basic error
+ #[inline]
+ pub fn new(input: I, kind: ErrorKind) -> Self {
+ Self { input, kind }
+ }
+
+ /// Translate the input type
+ #[inline]
+ pub fn map_input<I2: Clone, O: Fn(I) -> I2>(self, op: O) -> InputError<I2> {
+ InputError {
+ input: op(self.input),
+ kind: self.kind,
+ }
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'i, I: ToOwned> InputError<&'i I>
+where
+ <I as ToOwned>::Owned: Clone,
+{
+ /// Obtaining ownership
+ pub fn into_owned(self) -> InputError<<I as ToOwned>::Owned> {
+ self.map_input(ToOwned::to_owned)
+ }
+}
+
+impl<I: Clone> ParserError<I> for InputError<I> {
+ #[inline]
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
+ Self {
+ input: input.clone(),
+ kind,
+ }
+ }
+
+ #[inline]
+ fn append(self, _: &I, _: ErrorKind) -> Self {
+ self
+ }
+}
+
+impl<I: Clone, C> AddContext<I, C> for InputError<I> {}
+
+impl<I: Clone, E> FromExternalError<I, E> for InputError<I> {
+ /// Create a new error from an input position and an external error
+ #[inline]
+ fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self {
+ Self {
+ input: input.clone(),
+ kind,
+ }
+ }
+}
+
+impl<I: Clone> ErrorConvert<InputError<(I, usize)>> for InputError<I> {
+ #[inline]
+ fn convert(self) -> InputError<(I, usize)> {
+ InputError {
+ input: (self.input, 0),
+ kind: self.kind,
+ }
+ }
+}
+
+impl<I: Clone> ErrorConvert<InputError<I>> for InputError<(I, usize)> {
+ #[inline]
+ fn convert(self) -> InputError<I> {
+ InputError {
+ input: self.input.0,
+ kind: self.kind,
+ }
+ }
+}
+
+/// The Display implementation allows the `std::error::Error` implementation
+impl<I: Clone + fmt::Display> fmt::Display for InputError<I> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{} error starting at: {}",
+ self.kind.description(),
+ self.input
+ )
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static> std::error::Error
+ for InputError<I>
+{
+}
+
+impl<I> ParserError<I> for () {
+ #[inline]
+ fn from_error_kind(_: &I, _: ErrorKind) -> Self {}
+
+ #[inline]
+ fn append(self, _: &I, _: ErrorKind) -> Self {}
+}
+
+impl<I, C> AddContext<I, C> for () {}
+
+impl<I, E> FromExternalError<I, E> for () {
+ #[inline]
+ fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self {}
+}
+
+impl ErrorConvert<()> for () {
+ #[inline]
+ fn convert(self) {}
+}
+
+/// Accumulate context while backtracking errors
+#[derive(Debug)]
+pub struct ContextError<C = StrContext> {
+ #[cfg(feature = "alloc")]
+ context: crate::lib::std::vec::Vec<C>,
+ #[cfg(not(feature = "alloc"))]
+ context: core::marker::PhantomData<C>,
+ #[cfg(feature = "std")]
+ cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
+}
+
+impl<C> ContextError<C> {
+ /// Create an empty error
+ #[inline]
+ pub fn new() -> Self {
+ Self {
+ context: Default::default(),
+ #[cfg(feature = "std")]
+ cause: None,
+ }
+ }
+
+ /// Access context from [`Parser::context`]
+ #[inline]
+ #[cfg(feature = "alloc")]
+ pub fn context(&self) -> impl Iterator<Item = &C> {
+ self.context.iter()
+ }
+
+ /// Originating [`std::error::Error`]
+ #[inline]
+ #[cfg(feature = "std")]
+ pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
+ self.cause.as_deref()
+ }
+}
+
+impl<C: Clone> Clone for ContextError<C> {
+ fn clone(&self) -> Self {
+ Self {
+ context: self.context.clone(),
+ #[cfg(feature = "std")]
+ cause: self.cause.as_ref().map(|e| e.to_string().into()),
+ }
+ }
+}
+
+impl<C> Default for ContextError<C> {
+ #[inline]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<I, C> ParserError<I> for ContextError<C> {
+ #[inline]
+ fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self {
+ Self::new()
+ }
+
+ #[inline]
+ fn append(self, _input: &I, _kind: ErrorKind) -> Self {
+ self
+ }
+
+ #[inline]
+ fn or(self, other: Self) -> Self {
+ other
+ }
+}
+
+impl<C, I> AddContext<I, C> for ContextError<C> {
+ #[inline]
+ fn add_context(mut self, _input: &I, ctx: C) -> Self {
+ #[cfg(feature = "alloc")]
+ self.context.push(ctx);
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+impl<C, I, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E>
+ for ContextError<C>
+{
+ #[inline]
+ fn from_external_error(_input: &I, _kind: ErrorKind, e: E) -> Self {
+ let mut err = Self::new();
+ {
+ err.cause = Some(Box::new(e));
+ }
+ err
+ }
+}
+
+// HACK: This is more general than `std`, making the features non-additive
+#[cfg(not(feature = "std"))]
+impl<C, I, E: Send + Sync + 'static> FromExternalError<I, E> for ContextError<C> {
+ #[inline]
+ fn from_external_error(_input: &I, _kind: ErrorKind, _e: E) -> Self {
+ let err = Self::new();
+ err
+ }
+}
+
+// For tests
+impl<C: core::cmp::PartialEq> core::cmp::PartialEq for ContextError<C> {
+ fn eq(&self, other: &Self) -> bool {
+ #[cfg(feature = "alloc")]
+ {
+ if self.context != other.context {
+ return false;
+ }
+ }
+ #[cfg(feature = "std")]
+ {
+ if self.cause.as_ref().map(ToString::to_string)
+ != other.cause.as_ref().map(ToString::to_string)
+ {
+ return false;
+ }
+ }
+
+ true
+ }
+}
+
+impl crate::lib::std::fmt::Display for ContextError<StrContext> {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ #[cfg(feature = "alloc")]
+ {
+ let expression = self.context().find_map(|c| match c {
+ StrContext::Label(c) => Some(c),
+ _ => None,
+ });
+ let expected = self
+ .context()
+ .filter_map(|c| match c {
+ StrContext::Expected(c) => Some(c),
+ _ => None,
+ })
+ .collect::<crate::lib::std::vec::Vec<_>>();
+
+ let mut newline = false;
+
+ if let Some(expression) = expression {
+ newline = true;
+
+ write!(f, "invalid {}", expression)?;
+ }
+
+ if !expected.is_empty() {
+ if newline {
+ writeln!(f)?;
+ }
+ newline = true;
+
+ write!(f, "expected ")?;
+ for (i, expected) in expected.iter().enumerate() {
+ if i != 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", expected)?;
+ }
+ }
+ #[cfg(feature = "std")]
+ {
+ if let Some(cause) = self.cause() {
+ if newline {
+ writeln!(f)?;
+ }
+ write!(f, "{}", cause)?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+/// Additional parse context for [`ContextError`] added via [`Parser::context`]
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum StrContext {
+ /// Description of what is currently being parsed
+ Label(&'static str),
+ /// Grammar item that was expected
+ Expected(StrContextValue),
+}
+
+impl crate::lib::std::fmt::Display for StrContext {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ match self {
+ Self::Label(name) => write!(f, "invalid {name}"),
+ Self::Expected(value) => write!(f, "expected {value}"),
+ }
+ }
+}
+
+/// See [`StrContext`]
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum StrContextValue {
+ /// A [`char`] token
+ CharLiteral(char),
+ /// A [`&str`] token
+ StringLiteral(&'static str),
+ /// A description of what was being parsed
+ Description(&'static str),
+}
+
+impl From<char> for StrContextValue {
+ #[inline]
+ fn from(inner: char) -> Self {
+ Self::CharLiteral(inner)
+ }
+}
+
+impl From<&'static str> for StrContextValue {
+ #[inline]
+ fn from(inner: &'static str) -> Self {
+ Self::StringLiteral(inner)
+ }
+}
+
+impl crate::lib::std::fmt::Display for StrContextValue {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ match self {
+ Self::CharLiteral('\n') => "newline".fmt(f),
+ Self::CharLiteral('`') => "'`'".fmt(f),
+ Self::CharLiteral(c) if c.is_ascii_control() => {
+ write!(f, "`{}`", c.escape_debug())
+ }
+ Self::CharLiteral(c) => write!(f, "`{}`", c),
+ Self::StringLiteral(c) => write!(f, "`{}`", c),
+ Self::Description(c) => write!(f, "{}", c),
+ }
+ }
+}
+
+/// Trace all error paths, particularly for tests
+#[derive(Debug)]
+#[cfg(feature = "std")]
+pub enum TreeError<I, C = StrContext> {
+ /// Initial error that kicked things off
+ Base(TreeErrorBase<I>),
+ /// Traces added to the error while walking back up the stack
+ Stack {
+ /// Initial error that kicked things off
+ base: Box<Self>,
+ /// Traces added to the error while walking back up the stack
+ stack: Vec<TreeErrorFrame<I, C>>,
+ },
+ /// All failed branches of an `alt`
+ Alt(Vec<Self>),
+}
+
+/// See [`TreeError::Stack`]
+#[derive(Debug)]
+#[cfg(feature = "std")]
+pub enum TreeErrorFrame<I, C = StrContext> {
+ /// See [`ParserError::append`]
+ Kind(TreeErrorBase<I>),
+ /// See [`AddContext::add_context`]
+ Context(TreeErrorContext<I, C>),
+}
+
+/// See [`TreeErrorFrame::Kind`], [`ParserError::append`]
+#[derive(Debug)]
+#[cfg(feature = "std")]
+pub struct TreeErrorBase<I> {
+ /// Parsed input, at the location where the error occurred
+ pub input: I,
+ /// Debug context
+ pub kind: ErrorKind,
+ /// See [`FromExternalError::from_external_error`]
+ pub cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
+}
+
+/// See [`TreeErrorFrame::Context`], [`AddContext::add_context`]
+#[derive(Debug)]
+#[cfg(feature = "std")]
+pub struct TreeErrorContext<I, C = StrContext> {
+ /// Parsed input, at the location where the error occurred
+ pub input: I,
+ /// See [`AddContext::add_context`]
+ pub context: C,
+}
+
+#[cfg(feature = "std")]
+impl<'i, I: ToOwned, C> TreeError<&'i I, C>
+where
+ <I as ToOwned>::Owned: Clone,
+{
+ /// Obtaining ownership
+ pub fn into_owned(self) -> TreeError<<I as ToOwned>::Owned, C> {
+ self.map_input(ToOwned::to_owned)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I, C> TreeError<I, C>
+where
+ I: Clone,
+{
+ /// Translate the input type
+ pub fn map_input<I2: Clone, O: Clone + Fn(I) -> I2>(self, op: O) -> TreeError<I2, C> {
+ match self {
+ TreeError::Base(base) => TreeError::Base(TreeErrorBase {
+ input: op(base.input),
+ kind: base.kind,
+ cause: base.cause,
+ }),
+ TreeError::Stack { base, stack } => {
+ let base = Box::new(base.map_input(op.clone()));
+ let stack = stack
+ .into_iter()
+ .map(|frame| match frame {
+ TreeErrorFrame::Kind(kind) => TreeErrorFrame::Kind(TreeErrorBase {
+ input: op(kind.input),
+ kind: kind.kind,
+ cause: kind.cause,
+ }),
+ TreeErrorFrame::Context(context) => {
+ TreeErrorFrame::Context(TreeErrorContext {
+ input: op(context.input),
+ context: context.context,
+ })
+ }
+ })
+ .collect();
+ TreeError::Stack { base, stack }
+ }
+ TreeError::Alt(alt) => {
+ TreeError::Alt(alt.into_iter().map(|e| e.map_input(op.clone())).collect())
+ }
+ }
+ }
+
+ fn append_frame(self, frame: TreeErrorFrame<I, C>) -> Self {
+ match self {
+ TreeError::Stack { base, mut stack } => {
+ stack.push(frame);
+ TreeError::Stack { base, stack }
+ }
+ base => TreeError::Stack {
+ base: Box::new(base),
+ stack: vec![frame],
+ },
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I, C> ParserError<I> for TreeError<I, C>
+where
+ I: Clone,
+{
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
+ TreeError::Base(TreeErrorBase {
+ input: input.clone(),
+ kind,
+ cause: None,
+ })
+ }
+
+ fn append(self, input: &I, kind: ErrorKind) -> Self {
+ let frame = TreeErrorFrame::Kind(TreeErrorBase {
+ input: input.clone(),
+ kind,
+ cause: None,
+ });
+ self.append_frame(frame)
+ }
+
+ fn or(self, other: Self) -> Self {
+ match (self, other) {
+ (TreeError::Alt(mut first), TreeError::Alt(second)) => {
+ // Just in case an implementation does a divide-and-conquer algorithm
+ //
+ // To prevent mixing `alt`s at different levels, parsers should
+ // `alt_err.append(input, ErrorKind::Alt)`.
+ first.extend(second);
+ TreeError::Alt(first)
+ }
+ (TreeError::Alt(mut alt), new) | (new, TreeError::Alt(mut alt)) => {
+ alt.push(new);
+ TreeError::Alt(alt)
+ }
+ (first, second) => TreeError::Alt(vec![first, second]),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I, C> AddContext<I, C> for TreeError<I, C>
+where
+ I: Clone,
+{
+ fn add_context(self, input: &I, context: C) -> Self {
+ let frame = TreeErrorFrame::Context(TreeErrorContext {
+ input: input.clone(),
+ context,
+ });
+ self.append_frame(frame)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I, C, E: std::error::Error + Send + Sync + 'static> FromExternalError<I, E> for TreeError<I, C>
+where
+ I: Clone,
+{
+ fn from_external_error(input: &I, kind: ErrorKind, e: E) -> Self {
+ TreeError::Base(TreeErrorBase {
+ input: input.clone(),
+ kind,
+ cause: Some(Box::new(e)),
+ })
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I, C> TreeError<I, C>
+where
+ I: Clone + std::fmt::Display,
+ C: fmt::Display,
+{
+ fn write(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
+ let child_indent = indent + 2;
+ match self {
+ TreeError::Base(base) => {
+ writeln!(f, "{:indent$}{base}", "")?;
+ }
+ TreeError::Stack { base, stack } => {
+ base.write(f, indent)?;
+ for (level, frame) in stack.iter().enumerate() {
+ match frame {
+ TreeErrorFrame::Kind(frame) => {
+ writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
+ }
+ TreeErrorFrame::Context(frame) => {
+ writeln!(f, "{:child_indent$}{level}: {frame}", "")?;
+ }
+ }
+ }
+ }
+ TreeError::Alt(alt) => {
+ writeln!(f, "{:indent$}during one of:", "")?;
+ for child in alt {
+ child.write(f, child_indent)?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I: Clone + fmt::Display> fmt::Display for TreeErrorBase<I> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if let Some(cause) = self.cause.as_ref() {
+ write!(f, "caused by {cause}")?;
+ } else {
+ let kind = self.kind.description();
+ write!(f, "in {kind}")?;
+ }
+ let input = abbreviate(self.input.to_string());
+ write!(f, " at '{input}'")?;
+ Ok(())
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeErrorContext<I, C> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let context = &self.context;
+ let input = abbreviate(self.input.to_string());
+ write!(f, "{context} at '{input}'")?;
+ Ok(())
+ }
+}
+
+#[cfg(feature = "std")]
+impl<
+ I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
+ C: fmt::Display + fmt::Debug,
+ > std::error::Error for TreeError<I, C>
+{
+}
+
+#[cfg(feature = "std")]
+fn abbreviate(input: String) -> String {
+ let mut abbrev = None;
+
+ if let Some((line, _)) = input.split_once('\n') {
+ abbrev = Some(line);
+ }
+
+ let max_len = 20;
+ let current = abbrev.unwrap_or(&input);
+ if max_len < current.len() {
+ if let Some((index, _)) = current.char_indices().nth(max_len) {
+ abbrev = Some(&current[..index]);
+ }
+ }
+
+ if let Some(abbrev) = abbrev {
+ format!("{abbrev}...")
+ } else {
+ input
+ }
+}
+
+#[cfg(feature = "std")]
+impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for TreeError<I, C> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.write(f, 0)
+ }
+}
+
+/// Deprecated, replaced with [`ContextError`]
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct VerboseError<I: Clone, C = &'static str> {
+ /// Accumulated error information
+ pub errors: crate::lib::std::vec::Vec<(I, VerboseErrorKind<C>)>,
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<'i, I: ToOwned, C> VerboseError<&'i I, C>
+where
+ <I as ToOwned>::Owned: Clone,
+{
+ /// Obtaining ownership
+ pub fn into_owned(self) -> VerboseError<<I as ToOwned>::Owned, C> {
+ self.map_input(ToOwned::to_owned)
+ }
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<I: Clone, C> VerboseError<I, C> {
+ /// Translate the input type
+ pub fn map_input<I2: Clone, O>(self, op: O) -> VerboseError<I2, C>
+ where
+ O: Fn(I) -> I2,
+ {
+ VerboseError {
+ errors: self.errors.into_iter().map(|(i, k)| (op(i), k)).collect(),
+ }
+ }
+}
+
+/// Deprecated, replaced with [`ContextError`]
+#[cfg(feature = "std")]
+#[deprecated(since = "0.5.8", note = "Replaced with `ContextError`")]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum VerboseErrorKind<C = &'static str> {
+ /// Static string added by the `context` function
+ Context(C),
+ /// Error kind given by various parsers
+ Winnow(ErrorKind),
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<I: Clone, C> ParserError<I> for VerboseError<I, C> {
+ fn from_error_kind(input: &I, kind: ErrorKind) -> Self {
+ VerboseError {
+ errors: vec![(input.clone(), VerboseErrorKind::Winnow(kind))],
+ }
+ }
+
+ fn append(mut self, input: &I, kind: ErrorKind) -> Self {
+ self.errors
+ .push((input.clone(), VerboseErrorKind::Winnow(kind)));
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<I: Clone, C> AddContext<I, C> for VerboseError<I, C> {
+ fn add_context(mut self, input: &I, ctx: C) -> Self {
+ self.errors
+ .push((input.clone(), VerboseErrorKind::Context(ctx)));
+ self
+ }
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<I: Clone, C, E> FromExternalError<I, E> for VerboseError<I, C> {
+ /// Create a new error from an input position and an external error
+ fn from_external_error(input: &I, kind: ErrorKind, _e: E) -> Self {
+ Self::from_error_kind(input, kind)
+ }
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<I: Clone + fmt::Display, C: fmt::Display> fmt::Display for VerboseError<I, C> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ writeln!(f, "Parse error:")?;
+ for (input, error) in &self.errors {
+ match error {
+ VerboseErrorKind::Winnow(e) => writeln!(f, "{} at: {}", e.description(), input)?,
+ VerboseErrorKind::Context(s) => writeln!(f, "in section '{}', at: {}", s, input)?,
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "std")]
+#[allow(deprecated)]
+impl<
+ I: Clone + fmt::Debug + fmt::Display + Sync + Send + 'static,
+ C: fmt::Display + fmt::Debug,
+ > std::error::Error for VerboseError<I, C>
+{
+}
+
+/// Provide some minor debug context for errors
+#[rustfmt::skip]
+#[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)]
+#[allow(missing_docs)]
+pub enum ErrorKind {
+ Assert,
+ Token,
+ Tag,
+ Alt,
+ Many,
+ Eof,
+ Slice,
+ Complete,
+ Not,
+ Verify,
+ Fail,
+}
+
+impl ErrorKind {
+ #[rustfmt::skip]
+ /// Converts an `ErrorKind` to a text description
+ pub fn description(&self) -> &str {
+ match *self {
+ ErrorKind::Assert => "assert",
+ ErrorKind::Token => "token",
+ ErrorKind::Tag => "tag",
+ ErrorKind::Alt => "alternative",
+ ErrorKind::Many => "many",
+ ErrorKind::Eof => "end of file",
+ ErrorKind::Slice => "slice",
+ ErrorKind::Complete => "complete",
+ ErrorKind::Not => "negation",
+ ErrorKind::Verify => "predicate verification",
+ ErrorKind::Fail => "fail",
+ }
+ }
+}
+
+impl<I> ParserError<I> for ErrorKind {
+ #[inline]
+ fn from_error_kind(_input: &I, kind: ErrorKind) -> Self {
+ kind
+ }
+
+ #[inline]
+ fn append(self, _: &I, _: ErrorKind) -> Self {
+ self
+ }
+}
+
+impl<I, C> AddContext<I, C> for ErrorKind {}
+
+impl<I, E> FromExternalError<I, E> for ErrorKind {
+ /// Create a new error from an input position and an external error
+ #[inline]
+ fn from_external_error(_input: &I, kind: ErrorKind, _e: E) -> Self {
+ kind
+ }
+}
+
+/// The Display implementation allows the `std::error::Error` implementation
+impl fmt::Display for ErrorKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "error {:?}", self)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for ErrorKind {}
+
+/// See [`Parser::parse`]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ParseError<I, E> {
+ input: I,
+ offset: usize,
+ inner: E,
+}
+
+impl<I: Stream, E: ParserError<I>> ParseError<I, E> {
+ pub(crate) fn new(mut input: I, start: I::Checkpoint, inner: E) -> Self {
+ let offset = input.offset_from(&start);
+ input.reset(start);
+ Self {
+ input,
+ offset,
+ inner,
+ }
+ }
+}
+
+impl<I, E> ParseError<I, E> {
+ /// The [`Stream`] at the initial location when parsing started
+ #[inline]
+ pub fn input(&self) -> &I {
+ &self.input
+ }
+
+ /// The location in [`ParseError::input`] where parsing failed
+ #[inline]
+ pub fn offset(&self) -> usize {
+ self.offset
+ }
+
+ /// The original [`ParserError`]
+ #[inline]
+ pub fn inner(&self) -> &E {
+ &self.inner
+ }
+
+ /// The original [`ParserError`]
+ #[inline]
+ pub fn into_inner(self) -> E {
+ self.inner
+ }
+}
+
+impl<I, E> core::fmt::Display for ParseError<I, E>
+where
+ I: AsBStr,
+ E: core::fmt::Display,
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let input = self.input.as_bstr();
+ let span_start = self.offset;
+ let span_end = span_start;
+ #[cfg(feature = "std")]
+ if input.contains(&b'\n') {
+ let (line_idx, col_idx) = translate_position(input, span_start);
+ let line_num = line_idx + 1;
+ let col_num = col_idx + 1;
+ let gutter = line_num.to_string().len();
+ let content = input
+ .split(|c| *c == b'\n')
+ .nth(line_idx)
+ .expect("valid line number");
+
+ writeln!(f, "parse error at line {}, column {}", line_num, col_num)?;
+ // |
+ for _ in 0..=gutter {
+ write!(f, " ")?;
+ }
+ writeln!(f, "|")?;
+
+ // 1 | 00:32:00.a999999
+ write!(f, "{} | ", line_num)?;
+ writeln!(f, "{}", String::from_utf8_lossy(content))?;
+
+ // | ^
+ for _ in 0..=gutter {
+ write!(f, " ")?;
+ }
+ write!(f, "|")?;
+ for _ in 0..=col_idx {
+ write!(f, " ")?;
+ }
+ // The span will be empty at eof, so we need to make sure we always print at least
+ // one `^`
+ write!(f, "^")?;
+ for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
+ write!(f, "^")?;
+ }
+ writeln!(f)?;
+ } else {
+ let content = input;
+ writeln!(f, "{}", String::from_utf8_lossy(content))?;
+ for _ in 0..=span_start {
+ write!(f, " ")?;
+ }
+ // The span will be empty at eof, so we need to make sure we always print at least
+ // one `^`
+ write!(f, "^")?;
+ for _ in (span_start + 1)..(span_end.min(span_start + content.len())) {
+ write!(f, "^")?;
+ }
+ writeln!(f)?;
+ }
+ write!(f, "{}", self.inner)?;
+
+ Ok(())
+ }
+}
+
+#[cfg(feature = "std")]
+fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
+ if input.is_empty() {
+ return (0, index);
+ }
+
+ let safe_index = index.min(input.len() - 1);
+ let column_offset = index - safe_index;
+ let index = safe_index;
+
+ let nl = input[0..index]
+ .iter()
+ .rev()
+ .enumerate()
+ .find(|(_, b)| **b == b'\n')
+ .map(|(nl, _)| index - nl - 1);
+ let line_start = match nl {
+ Some(nl) => nl + 1,
+ None => 0,
+ };
+ let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
+ 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)
+ });
+);
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..5614b7f
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,249 @@
+//! > 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: <https://github.com/EmbarkStudios/rust-ecosystem/issues/59>
+// "-Dunsafe_code",
+#![warn(clippy::all)]
+#![warn(clippy::await_holding_lock)]
+#![warn(clippy::char_lit_as_u8)]
+#![warn(clippy::checked_conversions)]
+#![warn(clippy::dbg_macro)]
+#![warn(clippy::debug_assert_with_mut_call)]
+#![warn(clippy::doc_markdown)]
+#![warn(clippy::empty_enum)]
+#![warn(clippy::enum_glob_use)]
+#![warn(clippy::exit)]
+#![warn(clippy::expl_impl_clone_on_copy)]
+#![warn(clippy::explicit_deref_methods)]
+#![warn(clippy::explicit_into_iter_loop)]
+#![warn(clippy::fallible_impl_from)]
+#![warn(clippy::filter_map_next)]
+#![warn(clippy::flat_map_option)]
+#![warn(clippy::float_cmp_const)]
+#![warn(clippy::fn_params_excessive_bools)]
+#![warn(clippy::from_iter_instead_of_collect)]
+#![warn(clippy::if_let_mutex)]
+#![warn(clippy::implicit_clone)]
+#![warn(clippy::imprecise_flops)]
+#![warn(clippy::inefficient_to_string)]
+#![warn(clippy::invalid_upcast_comparisons)]
+#![warn(clippy::large_digit_groups)]
+#![warn(clippy::large_stack_arrays)]
+#![warn(clippy::large_types_passed_by_value)]
+#![warn(clippy::let_unit_value)]
+#![warn(clippy::linkedlist)]
+#![warn(clippy::lossy_float_literal)]
+#![warn(clippy::macro_use_imports)]
+#![warn(clippy::manual_ok_or)]
+#![warn(clippy::map_err_ignore)]
+#![warn(clippy::map_flatten)]
+#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::match_on_vec_items)]
+#![warn(clippy::match_same_arms)]
+#![warn(clippy::match_wild_err_arm)]
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![warn(clippy::mem_forget)]
+#![warn(clippy::mismatched_target_os)]
+#![warn(clippy::missing_enforced_import_renames)]
+#![warn(clippy::mut_mut)]
+#![warn(clippy::mutex_integer)]
+#![warn(clippy::needless_borrow)]
+#![warn(clippy::needless_continue)]
+#![warn(clippy::needless_for_each)]
+#![warn(clippy::option_option)]
+#![warn(clippy::path_buf_push_overwrite)]
+#![warn(clippy::ptr_as_ptr)]
+#![warn(clippy::rc_mutex)]
+#![warn(clippy::ref_option_ref)]
+#![warn(clippy::rest_pat_in_fully_bound_structs)]
+#![warn(clippy::same_functions_in_if_condition)]
+#![warn(clippy::semicolon_if_nothing_returned)]
+#![warn(clippy::single_match_else)]
+#![warn(clippy::string_add_assign)]
+#![warn(clippy::string_add)]
+#![warn(clippy::string_lit_as_bytes)]
+#![warn(clippy::string_to_string)]
+#![warn(clippy::todo)]
+#![warn(clippy::trait_duplication_in_bounds)]
+#![warn(clippy::unimplemented)]
+#![warn(clippy::unnested_or_patterns)]
+#![warn(clippy::unused_self)]
+#![warn(clippy::useless_transmute)]
+#![warn(clippy::verbose_file_reads)]
+#![warn(clippy::zero_sized_map_values)]
+#![warn(future_incompatible)]
+#![warn(nonstandard_style)]
+#![warn(rust_2018_idioms)]
+// END - Embark standard lints v6 for Rust 1.55+
+#![allow(clippy::branches_sharing_code)]
+#![allow(clippy::collapsible_else_if)]
+#![allow(clippy::if_same_then_else)]
+#![allow(clippy::bool_assert_comparison)]
+#![allow(clippy::let_and_return)]
+#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::map_unwrap_or)]
+#![allow(clippy::single_match_else)]
+#![allow(clippy::single_match)]
+#![allow(clippy::unnested_or_patterns)]
+#[cfg_attr(nightly, warn(rustdoc::missing_doc_code_examples))]
+#[cfg(feature = "alloc")]
+#[cfg_attr(test, macro_use)]
+extern crate alloc;
+#[cfg(doctest)]
+extern crate doc_comment;
+
+#[cfg(doctest)]
+doc_comment::doctest!("../README.md");
+
+/// Lib module to re-export everything needed from `std` or `core`/`alloc`. This is how `serde` does
+/// it, albeit there it is not public.
+#[doc(hidden)]
+pub(crate) mod lib {
+ /// `std` facade allowing `std`/`core` to be interchangeable. Reexports `alloc` crate optionally,
+ /// as well as `core` or `std`
+ #[cfg(not(feature = "std"))]
+ /// internal std exports for no_std compatibility
+ pub mod std {
+ #[doc(hidden)]
+ #[cfg(not(feature = "alloc"))]
+ pub use core::borrow;
+
+ #[cfg(feature = "alloc")]
+ #[doc(hidden)]
+ pub use alloc::{borrow, boxed, collections, string, vec};
+
+ #[doc(hidden)]
+ pub use core::{cmp, convert, fmt, hash, iter, mem, ops, option, result, slice, str};
+
+ /// internal reproduction of std prelude
+ #[doc(hidden)]
+ pub mod prelude {
+ pub use core::prelude as v1;
+ }
+ }
+
+ #[cfg(feature = "std")]
+ /// internal std exports for `no_std` compatibility
+ pub mod std {
+ #[doc(hidden)]
+ pub use std::{
+ alloc, borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, ops, option,
+ result, slice, str, string, vec,
+ };
+
+ /// internal reproduction of std prelude
+ #[doc(hidden)]
+ pub mod prelude {
+ pub use std::prelude as v1;
+ }
+ }
+}
+
+#[macro_use]
+mod macros;
+
+#[macro_use]
+pub mod error;
+
+mod parser;
+
+pub mod stream;
+
+pub mod ascii;
+pub mod binary;
+pub mod combinator;
+pub mod token;
+pub mod trace;
+
+#[cfg(feature = "unstable-doc")]
+pub mod _topic;
+#[cfg(feature = "unstable-doc")]
+pub mod _tutorial;
+
+/// Core concepts available for glob import
+///
+/// Including
+/// - [`StreamIsPartial`][crate::stream::StreamIsPartial]
+/// - [`Parser`]
+///
+/// ## Example
+///
+/// ```rust
+/// use winnow::prelude::*;
+///
+/// fn parse_data(input: &mut &str) -> PResult<u64> {
+/// // ...
+/// # winnow::ascii::dec_uint(input)
+/// }
+///
+/// fn main() {
+/// let result = parse_data.parse("100");
+/// assert_eq!(result, Ok(100));
+/// }
+/// ```
+pub mod prelude {
+ pub use crate::stream::StreamIsPartial as _;
+ pub use crate::IResult;
+ pub use crate::PResult;
+ pub use crate::Parser;
+}
+
+pub use error::IResult;
+pub use error::PResult;
+pub use parser::*;
+pub use stream::BStr;
+pub use stream::Bytes;
+pub use stream::Located;
+pub use stream::Partial;
+pub use stream::Stateful;
+pub use stream::Str;
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..b3078c6
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,78 @@
+/// `match` for parsers
+///
+/// When parsers have unique prefixes to test for, this offers better performance over
+/// [`alt`][crate::combinator::alt] though it might be at the cost of duplicating parts of your grammar
+/// if you needed to [`peek`][crate::combinator::peek].
+///
+/// For tight control over the error in a catch-all case, use [`fail`][crate::combinator::fail].
+///
+/// # Example
+///
+/// ```rust
+/// use winnow::prelude::*;
+/// use winnow::combinator::dispatch;
+/// # use winnow::token::any;
+/// # use winnow::combinator::peek;
+/// # use winnow::combinator::preceded;
+/// # use winnow::combinator::success;
+/// # use winnow::combinator::fail;
+///
+/// fn escaped(input: &mut &str) -> PResult<char> {
+/// preceded('\\', escape_seq_char).parse_next(input)
+/// }
+///
+/// fn escape_seq_char(input: &mut &str) -> PResult<char> {
+/// dispatch! {any;
+/// 'b' => success('\u{8}'),
+/// 'f' => success('\u{c}'),
+/// 'n' => success('\n'),
+/// 'r' => success('\r'),
+/// 't' => success('\t'),
+/// '\\' => success('\\'),
+/// '"' => success('"'),
+/// _ => fail::<_, char, _>,
+/// }
+/// .parse_next(input)
+/// }
+///
+/// assert_eq!(escaped.parse_peek("\\nHello"), Ok(("Hello", '\n')));
+/// ```
+#[macro_export]
+macro_rules! dispatch {
+ ($match_parser: expr; $( $pat:pat $(if $pred:expr)? => $expr: expr ),+ $(,)? ) => {
+ $crate::trace::trace("dispatch", move |i: &mut _|
+ {
+ use $crate::Parser;
+ let initial = $match_parser.parse_next(i)?;
+ match initial {
+ $(
+ $pat $(if $pred)? => $expr.parse_next(i),
+ )*
+ }
+ })
+ }
+}
+
+macro_rules! succ (
+ (0, $submac:ident ! ($($rest:tt)*)) => ($submac!(1, $($rest)*));
+ (1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*));
+ (2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*));
+ (3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*));
+ (4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*));
+ (5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*));
+ (6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*));
+ (7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*));
+ (8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*));
+ (9, $submac:ident ! ($($rest:tt)*)) => ($submac!(10, $($rest)*));
+ (10, $submac:ident ! ($($rest:tt)*)) => ($submac!(11, $($rest)*));
+ (11, $submac:ident ! ($($rest:tt)*)) => ($submac!(12, $($rest)*));
+ (12, $submac:ident ! ($($rest:tt)*)) => ($submac!(13, $($rest)*));
+ (13, $submac:ident ! ($($rest:tt)*)) => ($submac!(14, $($rest)*));
+ (14, $submac:ident ! ($($rest:tt)*)) => ($submac!(15, $($rest)*));
+ (15, $submac:ident ! ($($rest:tt)*)) => ($submac!(16, $($rest)*));
+ (16, $submac:ident ! ($($rest:tt)*)) => ($submac!(17, $($rest)*));
+ (17, $submac:ident ! ($($rest:tt)*)) => ($submac!(18, $($rest)*));
+ (18, $submac:ident ! ($($rest:tt)*)) => ($submac!(19, $($rest)*));
+ (19, $submac:ident ! ($($rest:tt)*)) => ($submac!(20, $($rest)*));
+ (20, $submac:ident ! ($($rest:tt)*)) => ($submac!(21, $($rest)*));
+);
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..b59e4cd
--- /dev/null
+++ b/src/parser.rs
@@ -0,0 +1,977 @@
+//! Basic types to build the parsers
+
+use crate::combinator::*;
+use crate::error::{AddContext, FromExternalError, IResult, PResult, ParseError, ParserError};
+use crate::stream::{AsChar, Compare, Location, ParseSlice, Stream, StreamIsPartial};
+
+/// Core trait for parsing
+///
+/// The simplest way to implement a `Parser` is with a function
+/// ```rust
+/// use winnow::prelude::*;
+///
+/// fn success(input: &mut &str) -> PResult<()> {
+/// let output = ();
+/// Ok(output)
+/// }
+///
+/// let (input, output) = success.parse_peek("Hello").unwrap();
+/// assert_eq!(input, "Hello"); // We didn't consume any input
+/// ```
+///
+/// which can be made stateful by returning a function
+/// ```rust
+/// use winnow::prelude::*;
+///
+/// fn success<O: Clone>(output: O) -> impl FnMut(&mut &str) -> PResult<O> {
+/// move |input: &mut &str| {
+/// let output = output.clone();
+/// Ok(output)
+/// }
+/// }
+///
+/// let (input, output) = success("World").parse_peek("Hello").unwrap();
+/// assert_eq!(input, "Hello"); // We didn't consume any input
+/// assert_eq!(output, "World");
+/// ```
+///
+/// Additionally, some basic types implement `Parser` as well, including
+/// - `u8` and `char`, see [`winnow::token::one_of`][crate::token::one_of]
+/// - `&[u8]` and `&str`, see [`winnow::token::tag`][crate::token::tag]
+pub trait Parser<I, O, E> {
+ /// Parse all of `input`, generating `O` from it
+ #[inline]
+ fn parse(&mut self, mut input: I) -> Result<O, ParseError<I, E>>
+ where
+ Self: core::marker::Sized,
+ I: Stream,
+ // Force users to deal with `Incomplete` when `StreamIsPartial<true>`
+ I: StreamIsPartial,
+ I: Clone,
+ E: ParserError<I>,
+ {
+ debug_assert!(
+ !I::is_partial_supported(),
+ "partial streams need to handle `ErrMode::Incomplete`"
+ );
+
+ let start = input.checkpoint();
+ let (o, _) = (self.by_ref(), crate::combinator::eof)
+ .parse_next(&mut input)
+ .map_err(|e| {
+ let e = e
+ .into_inner()
+ .expect("complete parsers should not report `ErrMode::Incomplete(_)`");
+ ParseError::new(input, start, e)
+ })?;
+ Ok(o)
+ }
+
+ /// Take tokens from the [`Stream`], turning it into the output
+ ///
+ /// This includes advancing the [`Stream`] to the next location.
+ ///
+ /// On error, `input` will be left pointing at the error location.
+ fn parse_next(&mut self, input: &mut I) -> PResult<O, E>;
+
+ /// Take tokens from the [`Stream`], turning it into the output
+ ///
+ /// This includes advancing the [`Stream`] to the next location.
+ #[inline(always)]
+ fn parse_peek(&mut self, mut input: I) -> IResult<I, O, E> {
+ match self.parse_next(&mut input) {
+ Ok(o) => Ok((input, o)),
+ Err(err) => Err(err),
+ }
+ }
+
+ /// Treat `&mut Self` as a parser
+ ///
+ /// This helps when needing to move a `Parser` when all you have is a `&mut Parser`.
+ ///
+ /// # Example
+ ///
+ /// Because parsers are `FnMut`, they can be called multiple times. This prevents moving `f`
+ /// into [`length_data`][crate::binary::length_data] and `g` into
+ /// [`Parser::complete_err`]:
+ /// ```rust,compile_fail
+ /// # use winnow::prelude::*;
+ /// # use winnow::Parser;
+ /// # use winnow::error::ParserError;
+ /// # use winnow::binary::length_data;
+ /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>(
+ /// mut f: impl Parser<&'i [u8], usize, E>,
+ /// mut g: impl Parser<&'i [u8], O, E>
+ /// ) -> impl Parser<&'i [u8], O, E> {
+ /// move |i: &mut &'i [u8]| {
+ /// let mut data = length_data(f).parse_next(i)?;
+ /// let o = g.complete_err().parse_next(&mut data)?;
+ /// Ok(o)
+ /// }
+ /// }
+ /// ```
+ ///
+ /// By adding `by_ref`, we can make this work:
+ /// ```rust
+ /// # use winnow::prelude::*;
+ /// # use winnow::Parser;
+ /// # use winnow::error::ParserError;
+ /// # use winnow::binary::length_data;
+ /// pub fn length_value<'i, O, E: ParserError<&'i [u8]>>(
+ /// mut f: impl Parser<&'i [u8], usize, E>,
+ /// mut g: impl Parser<&'i [u8], O, E>
+ /// ) -> impl Parser<&'i [u8], O, E> {
+ /// move |i: &mut &'i [u8]| {
+ /// let mut data = length_data(f.by_ref()).parse_next(i)?;
+ /// let o = g.by_ref().complete_err().parse_next(&mut data)?;
+ /// Ok(o)
+ /// }
+ /// }
+ /// ```
+ #[inline(always)]
+ fn by_ref(&mut self) -> ByRef<'_, Self>
+ where
+ Self: core::marker::Sized,
+ {
+ ByRef::new(self)
+ }
+
+ /// Produce the provided value
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::alpha1;
+ /// # fn main() {
+ ///
+ /// let mut parser = alpha1.value(1234);
+ ///
+ /// assert_eq!(parser.parse_peek("abcd"), Ok(("", 1234)));
+ /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice))));
+ /// # }
+ /// ```
+ #[doc(alias = "to")]
+ #[inline(always)]
+ fn value<O2>(self, val: O2) -> Value<Self, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ O2: Clone,
+ {
+ Value::new(self, val)
+ }
+
+ /// Discards the output of the `Parser`
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::alpha1;
+ /// # fn main() {
+ ///
+ /// let mut parser = alpha1.void();
+ ///
+ /// assert_eq!(parser.parse_peek("abcd"), Ok(("", ())));
+ /// assert_eq!(parser.parse_peek("123abcd;"), Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice))));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn void(self) -> Void<Self, I, O, E>
+ where
+ Self: core::marker::Sized,
+ {
+ Void::new(self)
+ }
+
+ /// Convert the parser's output to another type using [`std::convert::From`]
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::prelude::*;
+ /// # use winnow::error::InputError;
+ /// use winnow::ascii::alpha1;
+ /// # fn main() {
+ ///
+ /// fn parser1<'s>(i: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+ /// alpha1(i)
+ /// }
+ ///
+ /// let mut parser2 = parser1.output_into();
+ ///
+ /// // the parser converts the &str output of the child parser into a Vec<u8>
+ /// let bytes: IResult<&str, Vec<u8>> = parser2.parse_peek("abcd");
+ /// assert_eq!(bytes, Ok(("", vec![97, 98, 99, 100])));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn output_into<O2>(self) -> OutputInto<Self, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ O: Into<O2>,
+ {
+ OutputInto::new(self)
+ }
+
+ /// Produce the consumed input as produced value.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::{alpha1};
+ /// use winnow::combinator::separated_pair;
+ /// # fn main() {
+ ///
+ /// let mut parser = separated_pair(alpha1, ',', alpha1).recognize();
+ ///
+ /// assert_eq!(parser.parse_peek("abcd,efgh"), Ok(("", "abcd,efgh")));
+ /// assert_eq!(parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify))));
+ /// # }
+ /// ```
+ #[doc(alias = "concat")]
+ #[inline(always)]
+ fn recognize(self) -> Recognize<Self, I, O, E>
+ where
+ Self: core::marker::Sized,
+ I: Stream,
+ {
+ Recognize::new(self)
+ }
+
+ /// Produce the consumed input with the output
+ ///
+ /// Functions similarly to [recognize][Parser::recognize] except it
+ /// returns the parser output as well.
+ ///
+ /// This can be useful especially in cases where the output is not the same type
+ /// as the input, or the input is a user defined type.
+ ///
+ /// Returned tuple is of the format `(produced output, consumed input)`.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::prelude::*;
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError};
+ /// use winnow::ascii::{alpha1};
+ /// use winnow::token::tag;
+ /// use winnow::combinator::separated_pair;
+ ///
+ /// fn inner_parser<'s>(input: &mut &'s str) -> PResult<bool, InputError<&'s str>> {
+ /// "1234".value(true).parse_next(input)
+ /// }
+ ///
+ /// let mut consumed_parser = separated_pair(alpha1, ',', alpha1).value(true).with_recognized();
+ ///
+ /// assert_eq!(consumed_parser.parse_peek("abcd,efgh1"), Ok(("1", (true, "abcd,efgh"))));
+ /// assert_eq!(consumed_parser.parse_peek("abcd;"),Err(ErrMode::Backtrack(InputError::new(";", ErrorKind::Verify))));
+ ///
+ /// // the second output (representing the consumed input)
+ /// // should be the same as that of the `recognize` parser.
+ /// let mut recognize_parser = inner_parser.recognize();
+ /// let mut consumed_parser = inner_parser.with_recognized().map(|(output, consumed)| consumed);
+ ///
+ /// assert_eq!(recognize_parser.parse_peek("1234"), consumed_parser.parse_peek("1234"));
+ /// assert_eq!(recognize_parser.parse_peek("abcd"), consumed_parser.parse_peek("abcd"));
+ /// ```
+ #[doc(alias = "consumed")]
+ #[inline(always)]
+ fn with_recognized(self) -> WithRecognized<Self, I, O, E>
+ where
+ Self: core::marker::Sized,
+ I: Stream,
+ {
+ WithRecognized::new(self)
+ }
+
+ /// Produce the location of the consumed input as produced value.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::prelude::*;
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream};
+ /// use winnow::stream::Located;
+ /// use winnow::ascii::alpha1;
+ /// use winnow::combinator::separated_pair;
+ ///
+ /// let mut parser = separated_pair(alpha1.span(), ',', alpha1.span());
+ ///
+ /// assert_eq!(parser.parse(Located::new("abcd,efgh")), Ok((0..4, 5..9)));
+ /// assert_eq!(parser.parse_peek(Located::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(Located::new("abcd;").peek_slice(4).0, ErrorKind::Verify))));
+ /// ```
+ #[inline(always)]
+ fn span(self) -> Span<Self, I, O, E>
+ where
+ Self: core::marker::Sized,
+ I: Stream + Location,
+ {
+ Span::new(self)
+ }
+
+ /// Produce the location of consumed input with the output
+ ///
+ /// Functions similarly to [`Parser::span`] except it
+ /// returns the parser output as well.
+ ///
+ /// This can be useful especially in cases where the output is not the same type
+ /// as the input, or the input is a user defined type.
+ ///
+ /// Returned tuple is of the format `(produced output, consumed input)`.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::prelude::*;
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, stream::Stream};
+ /// use winnow::stream::Located;
+ /// use winnow::ascii::alpha1;
+ /// use winnow::token::tag;
+ /// use winnow::combinator::separated_pair;
+ ///
+ /// fn inner_parser<'s>(input: &mut Located<&'s str>) -> PResult<bool, InputError<Located<&'s str>>> {
+ /// "1234".value(true).parse_next(input)
+ /// }
+ ///
+ /// # fn main() {
+ ///
+ /// let mut consumed_parser = separated_pair(alpha1.value(1).with_span(), ',', alpha1.value(2).with_span());
+ ///
+ /// assert_eq!(consumed_parser.parse(Located::new("abcd,efgh")), Ok(((1, 0..4), (2, 5..9))));
+ /// assert_eq!(consumed_parser.parse_peek(Located::new("abcd;")),Err(ErrMode::Backtrack(InputError::new(Located::new("abcd;").peek_slice(4).0, ErrorKind::Verify))));
+ ///
+ /// // the second output (representing the consumed input)
+ /// // should be the same as that of the `span` parser.
+ /// let mut recognize_parser = inner_parser.span();
+ /// let mut consumed_parser = inner_parser.with_span().map(|(output, consumed)| consumed);
+ ///
+ /// assert_eq!(recognize_parser.parse_peek(Located::new("1234")), consumed_parser.parse_peek(Located::new("1234")));
+ /// assert_eq!(recognize_parser.parse_peek(Located::new("abcd")), consumed_parser.parse_peek(Located::new("abcd")));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn with_span(self) -> WithSpan<Self, I, O, E>
+ where
+ Self: core::marker::Sized,
+ I: Stream + Location,
+ {
+ WithSpan::new(self)
+ }
+
+ /// Maps a function over the output of a parser
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::digit1;
+ /// # fn main() {
+ ///
+ /// let mut parser = digit1.map(|s: &str| s.len());
+ ///
+ /// // the parser will count how many characters were returned by digit1
+ /// assert_eq!(parser.parse_peek("123456"), Ok(("", 6)));
+ ///
+ /// // this will fail if digit1 fails
+ /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice))));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn map<G, O2>(self, map: G) -> Map<Self, G, I, O, O2, E>
+ where
+ G: Fn(O) -> O2,
+ Self: core::marker::Sized,
+ {
+ Map::new(self, map)
+ }
+
+ /// Applies a function returning a `Result` over the output of a parser.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::digit1;
+ /// # fn main() {
+ ///
+ /// let mut parse = digit1.try_map(|s: &str| s.parse::<u8>());
+ ///
+ /// // the parser will convert the result of digit1 to a number
+ /// assert_eq!(parse.parse_peek("123"), Ok(("", 123)));
+ ///
+ /// // this will fail if digit1 fails
+ /// assert_eq!(parse.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice))));
+ ///
+ /// // this will fail if the mapped function fails (a `u8` is too small to hold `123456`)
+ /// assert_eq!(parse.parse_peek("123456"), Err(ErrMode::Backtrack(InputError::new("123456", ErrorKind::Verify))));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn try_map<G, O2, E2>(self, map: G) -> TryMap<Self, G, I, O, O2, E, E2>
+ where
+ Self: core::marker::Sized,
+ G: FnMut(O) -> Result<O2, E2>,
+ I: Stream,
+ E: FromExternalError<I, E2>,
+ {
+ TryMap::new(self, map)
+ }
+
+ /// Apply both [`Parser::verify`] and [`Parser::map`].
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::digit1;
+ /// # fn main() {
+ ///
+ /// let mut parse = digit1.verify_map(|s: &str| s.parse::<u8>().ok());
+ ///
+ /// // the parser will convert the result of digit1 to a number
+ /// assert_eq!(parse.parse_peek("123"), Ok(("", 123)));
+ ///
+ /// // this will fail if digit1 fails
+ /// assert_eq!(parse.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice))));
+ ///
+ /// // this will fail if the mapped function fails (a `u8` is too small to hold `123456`)
+ /// assert_eq!(parse.parse_peek("123456"), Err(ErrMode::Backtrack(InputError::new("123456", ErrorKind::Verify))));
+ /// # }
+ /// ```
+ #[doc(alias = "satisfy_map")]
+ #[doc(alias = "filter_map")]
+ #[doc(alias = "map_opt")]
+ #[inline(always)]
+ fn verify_map<G, O2>(self, map: G) -> VerifyMap<Self, G, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ G: FnMut(O) -> Option<O2>,
+ I: Stream,
+ E: ParserError<I>,
+ {
+ VerifyMap::new(self, map)
+ }
+
+ /// Creates a parser from the output of this one
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser};
+ /// use winnow::token::take;
+ /// use winnow::binary::u8;
+ ///
+ /// fn length_data<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+ /// u8.flat_map(take).parse_next(input)
+ /// }
+ ///
+ /// assert_eq!(length_data.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..])));
+ /// assert_eq!(length_data.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice))));
+ /// ```
+ ///
+ /// which is the same as
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, PResult, Parser};
+ /// use winnow::token::take;
+ /// use winnow::binary::u8;
+ ///
+ /// fn length_data<'s>(input: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+ /// let length = u8.parse_next(input)?;
+ /// let data = take(length).parse_next(input)?;
+ /// Ok(data)
+ /// }
+ ///
+ /// assert_eq!(length_data.parse_peek(&[2, 0, 1, 2][..]), Ok((&[2][..], &[0, 1][..])));
+ /// assert_eq!(length_data.parse_peek(&[4, 0, 1, 2][..]), Err(ErrMode::Backtrack(InputError::new(&[0, 1, 2][..], ErrorKind::Slice))));
+ /// ```
+ #[inline(always)]
+ fn flat_map<G, H, O2>(self, map: G) -> FlatMap<Self, G, H, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ G: FnMut(O) -> H,
+ H: Parser<I, O2, E>,
+ {
+ FlatMap::new(self, map)
+ }
+
+ /// Applies a second parser over the output of the first one
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::digit1;
+ /// use winnow::token::take;
+ /// # fn main() {
+ ///
+ /// let mut digits = take(5u8).and_then(digit1);
+ ///
+ /// assert_eq!(digits.parse_peek("12345"), Ok(("", "12345")));
+ /// assert_eq!(digits.parse_peek("123ab"), Ok(("", "123")));
+ /// assert_eq!(digits.parse_peek("123"), Err(ErrMode::Backtrack(InputError::new("123", ErrorKind::Slice))));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn and_then<G, O2>(self, inner: G) -> AndThen<Self, G, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ G: Parser<O, O2, E>,
+ O: StreamIsPartial,
+ I: Stream,
+ {
+ AndThen::new(self, inner)
+ }
+
+ /// Apply [`std::str::FromStr`] to the output of the parser
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::prelude::*;
+ /// use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// use winnow::ascii::digit1;
+ ///
+ /// fn parser<'s>(input: &mut &'s str) -> PResult<u64, InputError<&'s str>> {
+ /// digit1.parse_to().parse_next(input)
+ /// }
+ ///
+ /// // the parser will count how many characters were returned by digit1
+ /// assert_eq!(parser.parse_peek("123456"), Ok(("", 123456)));
+ ///
+ /// // this will fail if digit1 fails
+ /// assert_eq!(parser.parse_peek("abc"), Err(ErrMode::Backtrack(InputError::new("abc", ErrorKind::Slice))));
+ /// ```
+ #[doc(alias = "from_str")]
+ #[inline(always)]
+ fn parse_to<O2>(self) -> ParseTo<Self, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ I: Stream,
+ O: ParseSlice<O2>,
+ E: ParserError<I>,
+ {
+ ParseTo::new(self)
+ }
+
+ /// Returns the output of the child parser if it satisfies a verification function.
+ ///
+ /// The verification function takes as argument a reference to the output of the
+ /// parser.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode,error::ErrorKind, error::InputError, Parser};
+ /// # use winnow::ascii::alpha1;
+ /// # fn main() {
+ ///
+ /// let mut parser = alpha1.verify(|s: &str| s.len() == 4);
+ ///
+ /// assert_eq!(parser.parse_peek("abcd"), Ok(("", "abcd")));
+ /// assert_eq!(parser.parse_peek("abcde"), Err(ErrMode::Backtrack(InputError::new("abcde", ErrorKind::Verify))));
+ /// assert_eq!(parser.parse_peek("123abcd;"),Err(ErrMode::Backtrack(InputError::new("123abcd;", ErrorKind::Slice))));
+ /// # }
+ /// ```
+ #[doc(alias = "satisfy")]
+ #[doc(alias = "filter")]
+ #[inline(always)]
+ fn verify<G, O2>(self, filter: G) -> Verify<Self, G, I, O, O2, E>
+ where
+ Self: core::marker::Sized,
+ G: Fn(&O2) -> bool,
+ I: Stream,
+ O: crate::lib::std::borrow::Borrow<O2>,
+ O2: ?Sized,
+ E: ParserError<I>,
+ {
+ Verify::new(self, filter)
+ }
+
+ /// If parsing fails, add context to the error
+ ///
+ /// This is used mainly to add user friendly information
+ /// to errors when backtracking through a parse tree.
+ #[doc(alias = "labelled")]
+ #[inline(always)]
+ fn context<C>(self, context: C) -> Context<Self, I, O, E, C>
+ where
+ Self: core::marker::Sized,
+ I: Stream,
+ E: AddContext<I, C>,
+ C: Clone + crate::lib::std::fmt::Debug,
+ {
+ Context::new(self, context)
+ }
+
+ /// Transforms [`Incomplete`][crate::error::ErrMode::Incomplete] into [`Backtrack`][crate::error::ErrMode::Backtrack]
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, stream::Partial, Parser};
+ /// # use winnow::token::take;
+ /// # fn main() {
+ ///
+ /// let mut parser = take(5u8).complete_err();
+ ///
+ /// assert_eq!(parser.parse_peek(Partial::new("abcdefg")), Ok((Partial::new("fg"), "abcde")));
+ /// assert_eq!(parser.parse_peek(Partial::new("abcd")), Err(ErrMode::Backtrack(InputError::new(Partial::new("abcd"), ErrorKind::Complete))));
+ /// # }
+ /// ```
+ #[inline(always)]
+ fn complete_err(self) -> CompleteErr<Self>
+ where
+ Self: core::marker::Sized,
+ {
+ CompleteErr::new(self)
+ }
+
+ /// Convert the parser's error to another type using [`std::convert::From`]
+ #[inline(always)]
+ fn err_into<E2>(self) -> ErrInto<Self, I, O, E, E2>
+ where
+ Self: core::marker::Sized,
+ E: Into<E2>,
+ {
+ ErrInto::new(self)
+ }
+}
+
+impl<'a, I, O, E, F> Parser<I, O, E> for F
+where
+ F: FnMut(&mut I) -> PResult<O, E> + 'a,
+ I: Stream,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+ self(i)
+ }
+}
+
+/// This is a shortcut for [`one_of`][crate::token::one_of].
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{ErrorKind, InputError}};
+/// fn parser<'s>(i: &mut &'s [u8]) -> PResult<u8, InputError<&'s [u8]>> {
+/// b'a'.parse_next(i)
+/// }
+/// assert_eq!(parser.parse_peek(&b"abc"[..]), Ok((&b"bc"[..], b'a')));
+/// assert_eq!(parser.parse_peek(&b" abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b" abc"[..], ErrorKind::Verify))));
+/// assert_eq!(parser.parse_peek(&b"bc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"bc"[..], ErrorKind::Verify))));
+/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Token))));
+/// ```
+impl<I, E> Parser<I, u8, E> for u8
+where
+ I: StreamIsPartial,
+ I: Stream<Token = u8>,
+ E: ParserError<I>,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<u8, E> {
+ crate::token::one_of(*self).parse_next(i)
+ }
+}
+
+/// This is a shortcut for [`one_of`][crate::token::one_of].
+///
+/// # Example
+///
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{ErrorKind, InputError}};
+/// fn parser<'s>(i: &mut &'s str) -> PResult<char, InputError<&'s str>> {
+/// 'a'.parse_next(i)
+/// }
+/// assert_eq!(parser.parse_peek("abc"), Ok(("bc", 'a')));
+/// assert_eq!(parser.parse_peek(" abc"), Err(ErrMode::Backtrack(InputError::new(" abc", ErrorKind::Verify))));
+/// assert_eq!(parser.parse_peek("bc"), Err(ErrMode::Backtrack(InputError::new("bc", ErrorKind::Verify))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// ```
+impl<I, E> Parser<I, <I as Stream>::Token, E> for char
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: AsChar + Clone,
+ E: ParserError<I>,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Token, E> {
+ crate::token::one_of(*self).parse_next(i)
+ }
+}
+
+/// This is a shortcut for [`tag`][crate::token::tag].
+///
+/// # Example
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::combinator::alt;
+/// # use winnow::token::take;
+///
+/// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+/// alt((&"Hello"[..], take(5usize))).parse_next(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..])));
+/// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..])));
+/// assert_eq!(parser.parse_peek(&b"Some"[..]), Err(ErrMode::Backtrack(InputError::new(&b"Some"[..], ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Slice))));
+/// ```
+impl<'s, I, E: ParserError<I>> Parser<I, <I as Stream>::Slice, E> for &'s [u8]
+where
+ I: Compare<&'s [u8]> + StreamIsPartial,
+ I: Stream,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
+ crate::token::tag(*self).parse_next(i)
+ }
+}
+
+/// This is a shortcut for [`tag`][crate::token::tag].
+///
+/// # Example
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::combinator::alt;
+/// # use winnow::token::take;
+///
+/// fn parser<'s>(s: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+/// alt((b"Hello", take(5usize))).parse_next(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek(&b"Hello, World!"[..]), Ok((&b", World!"[..], &b"Hello"[..])));
+/// assert_eq!(parser.parse_peek(&b"Something"[..]), Ok((&b"hing"[..], &b"Somet"[..])));
+/// assert_eq!(parser.parse_peek(&b"Some"[..]), Err(ErrMode::Backtrack(InputError::new(&b"Some"[..], ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(&b""[..]), Err(ErrMode::Backtrack(InputError::new(&b""[..], ErrorKind::Slice))));
+/// ```
+impl<'s, I, E: ParserError<I>, const N: usize> Parser<I, <I as Stream>::Slice, E> for &'s [u8; N]
+where
+ I: Compare<&'s [u8; N]> + StreamIsPartial,
+ I: Stream,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
+ crate::token::tag(*self).parse_next(i)
+ }
+}
+
+/// This is a shortcut for [`tag`][crate::token::tag].
+///
+/// # Example
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}};
+/// # use winnow::combinator::alt;
+/// # use winnow::token::take;
+///
+/// fn parser<'s>(s: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// alt(("Hello", take(5usize))).parse_next(s)
+/// }
+///
+/// assert_eq!(parser.parse_peek("Hello, World!"), Ok((", World!", "Hello")));
+/// assert_eq!(parser.parse_peek("Something"), Ok(("hing", "Somet")));
+/// assert_eq!(parser.parse_peek("Some"), Err(ErrMode::Backtrack(InputError::new("Some", ErrorKind::Slice))));
+/// assert_eq!(parser.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+impl<'s, I, E: ParserError<I>> Parser<I, <I as Stream>::Slice, E> for &'s str
+where
+ I: Compare<&'s str> + StreamIsPartial,
+ I: Stream,
+{
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<<I as Stream>::Slice, E> {
+ crate::token::tag(*self).parse_next(i)
+ }
+}
+
+impl<I, E: ParserError<I>> Parser<I, (), E> for () {
+ #[inline(always)]
+ fn parse_next(&mut self, _i: &mut I) -> PResult<(), E> {
+ Ok(())
+ }
+}
+
+macro_rules! impl_parser_for_tuple {
+ ($($parser:ident $output:ident),+) => (
+ #[allow(non_snake_case)]
+ impl<I, $($output),+, E: ParserError<I>, $($parser),+> Parser<I, ($($output),+,), E> for ($($parser),+,)
+ where
+ $($parser: Parser<I, $output, E>),+
+ {
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<($($output),+,), E> {
+ let ($(ref mut $parser),+,) = *self;
+
+ $(let $output = $parser.parse_next(i)?;)+
+
+ Ok(($($output),+,))
+ }
+ }
+ )
+}
+
+macro_rules! impl_parser_for_tuples {
+ ($parser1:ident $output1:ident, $($parser:ident $output:ident),+) => {
+ impl_parser_for_tuples!(__impl $parser1 $output1; $($parser $output),+);
+ };
+ (__impl $($parser:ident $output:ident),+; $parser1:ident $output1:ident $(,$parser2:ident $output2:ident)*) => {
+ impl_parser_for_tuple!($($parser $output),+);
+ impl_parser_for_tuples!(__impl $($parser $output),+, $parser1 $output1; $($parser2 $output2),*);
+ };
+ (__impl $($parser:ident $output:ident),+;) => {
+ impl_parser_for_tuple!($($parser $output),+);
+ }
+}
+
+impl_parser_for_tuples!(
+ P1 O1,
+ P2 O2,
+ P3 O3,
+ P4 O4,
+ P5 O5,
+ P6 O6,
+ P7 O7,
+ P8 O8,
+ P9 O9,
+ P10 O10,
+ P11 O11,
+ P12 O12,
+ P13 O13,
+ P14 O14,
+ P15 O15,
+ P16 O16,
+ P17 O17,
+ P18 O18,
+ P19 O19,
+ P20 O20,
+ P21 O21
+);
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+
+#[cfg(feature = "alloc")]
+impl<'a, I, O, E> Parser<I, O, E> for Box<dyn Parser<I, O, E> + 'a> {
+ #[inline(always)]
+ fn parse_next(&mut self, i: &mut I) -> PResult<O, E> {
+ (**self).parse_next(i)
+ }
+}
+
+/// Convert a [`Parser::parse_peek`] style parse function to be a [`Parser`]
+#[inline(always)]
+pub fn unpeek<'a, I, O, E>(
+ mut peek: impl FnMut(I) -> IResult<I, O, E> + 'a,
+) -> impl FnMut(&mut I) -> PResult<O, E>
+where
+ I: Clone,
+{
+ move |input| match peek((*input).clone()) {
+ Ok((i, o)) => {
+ *input = i;
+ Ok(o)
+ }
+ Err(err) => Err(err),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::binary::be_u16;
+ use crate::error::ErrMode;
+ use crate::error::ErrorKind;
+ use crate::error::InputError;
+ use crate::error::Needed;
+ use crate::token::take;
+ use crate::Partial;
+
+ #[doc(hidden)]
+ #[macro_export]
+ macro_rules! assert_size (
+ ($t:ty, $sz:expr) => (
+ assert!($crate::lib::std::mem::size_of::<$t>() <= $sz, "{} <= {} failed", $crate::lib::std::mem::size_of::<$t>(), $sz);
+ );
+ );
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn size_test() {
+ assert_size!(IResult<&[u8], &[u8], (&[u8], u32)>, 40);
+ assert_size!(IResult<&str, &str, u32>, 40);
+ assert_size!(Needed, 8);
+ assert_size!(ErrMode<u32>, 16);
+ assert_size!(ErrorKind, 1);
+ }
+
+ #[test]
+ fn err_map_test() {
+ let e = ErrMode::Backtrack(1);
+ assert_eq!(e.map(|v| v + 1), ErrMode::Backtrack(2));
+ }
+
+ #[test]
+ fn single_element_tuples() {
+ use crate::ascii::alpha1;
+ use crate::error::ErrorKind;
+
+ let mut parser = (alpha1,);
+ assert_eq!(parser.parse_peek("abc123def"), Ok(("123def", ("abc",))));
+ assert_eq!(
+ parser.parse_peek("123def"),
+ Err(ErrMode::Backtrack(InputError::new(
+ "123def",
+ ErrorKind::Slice
+ )))
+ );
+ }
+
+ #[test]
+ fn tuple_test() {
+ #[allow(clippy::type_complexity)]
+ fn tuple_3(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, (u16, &[u8], &[u8])> {
+ (be_u16, take(3u8), "fg").parse_peek(i)
+ }
+
+ assert_eq!(
+ tuple_3(Partial::new(&b"abcdefgh"[..])),
+ Ok((
+ Partial::new(&b"h"[..]),
+ (0x6162u16, &b"cde"[..], &b"fg"[..])
+ ))
+ );
+ assert_eq!(
+ tuple_3(Partial::new(&b"abcd"[..])),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ tuple_3(Partial::new(&b"abcde"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ tuple_3(Partial::new(&b"abcdejk"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"jk"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ }
+
+ #[test]
+ fn unit_type() {
+ fn parser(i: &mut &str) -> PResult<()> {
+ ().parse_next(i)
+ }
+ assert_eq!(parser.parse_peek("abxsbsh"), Ok(("abxsbsh", ())));
+ assert_eq!(parser.parse_peek("sdfjakdsas"), Ok(("sdfjakdsas", ())));
+ assert_eq!(parser.parse_peek(""), Ok(("", ())));
+ }
+}
diff --git a/src/stream/impls.rs b/src/stream/impls.rs
new file mode 100644
index 0000000..b277dd9
--- /dev/null
+++ b/src/stream/impls.rs
@@ -0,0 +1,537 @@
+macro_rules! impl_partial_eq {
+ ($lhs:ty, $rhs:ty) => {
+ impl<'a, 'b> PartialEq<$rhs> for $lhs {
+ #[inline]
+ fn eq(&self, other: &$rhs) -> bool {
+ let l = self.as_ref();
+ let r: &Self = other.as_ref();
+ PartialEq::eq(l, r)
+ }
+ }
+
+ impl<'a, 'b> PartialEq<$lhs> for $rhs {
+ #[inline]
+ fn eq(&self, other: &$lhs) -> bool {
+ PartialEq::eq(other, self)
+ }
+ }
+ };
+}
+
+macro_rules! impl_partial_ord {
+ ($lhs:ty, $rhs:ty) => {
+ impl<'a, 'b> PartialOrd<$rhs> for $lhs {
+ #[inline]
+ fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
+ let l = self.as_ref();
+ let r: &Self = other.as_ref();
+ PartialOrd::partial_cmp(l, r)
+ }
+ }
+
+ impl<'a, 'b> PartialOrd<$lhs> for $rhs {
+ #[inline]
+ fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
+ PartialOrd::partial_cmp(other, self)
+ }
+ }
+ };
+}
+
+mod bytes {
+ use crate::lib::std::{cmp::Ordering, fmt, ops};
+
+ use crate::stream::Bytes;
+
+ impl fmt::Display for Bytes {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as fmt::UpperHex>::fmt(self, f)
+ }
+ }
+
+ impl fmt::Debug for Bytes {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <Self as fmt::UpperHex>::fmt(self, f)
+ }
+ }
+
+ impl fmt::LowerHex for Bytes {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for byte in self.as_bytes() {
+ write!(f, "{:0>2x}", byte)?;
+ }
+ Ok(())
+ }
+ }
+
+ impl fmt::UpperHex for Bytes {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for (i, byte) in self.as_bytes().iter().enumerate() {
+ if 0 < i {
+ let absolute = (self.as_bytes().as_ptr() as usize) + i;
+ if f.alternate() && absolute != 0 && absolute % 4 == 0 {
+ write!(f, "_")?;
+ }
+ }
+ write!(f, "{:0>2X}", byte)?;
+ }
+ Ok(())
+ }
+ }
+
+ impl ops::Deref for Bytes {
+ type Target = [u8];
+
+ #[inline]
+ fn deref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+ }
+
+ impl ops::Index<usize> for Bytes {
+ type Output = u8;
+
+ #[inline]
+ fn index(&self, idx: usize) -> &u8 {
+ &self.as_bytes()[idx]
+ }
+ }
+
+ impl ops::Index<ops::RangeFull> for Bytes {
+ type Output = Bytes;
+
+ #[inline]
+ fn index(&self, _: ops::RangeFull) -> &Bytes {
+ self
+ }
+ }
+
+ impl ops::Index<ops::Range<usize>> for Bytes {
+ type Output = Bytes;
+
+ #[inline]
+ fn index(&self, r: ops::Range<usize>) -> &Bytes {
+ Bytes::new(&self.as_bytes()[r.start..r.end])
+ }
+ }
+
+ impl ops::Index<ops::RangeInclusive<usize>> for Bytes {
+ type Output = Bytes;
+
+ #[inline]
+ fn index(&self, r: ops::RangeInclusive<usize>) -> &Bytes {
+ Bytes::new(&self.as_bytes()[*r.start()..=*r.end()])
+ }
+ }
+
+ impl ops::Index<ops::RangeFrom<usize>> for Bytes {
+ type Output = Bytes;
+
+ #[inline]
+ fn index(&self, r: ops::RangeFrom<usize>) -> &Bytes {
+ Bytes::new(&self.as_bytes()[r.start..])
+ }
+ }
+
+ impl ops::Index<ops::RangeTo<usize>> for Bytes {
+ type Output = Bytes;
+
+ #[inline]
+ fn index(&self, r: ops::RangeTo<usize>) -> &Bytes {
+ Bytes::new(&self.as_bytes()[..r.end])
+ }
+ }
+
+ impl ops::Index<ops::RangeToInclusive<usize>> for Bytes {
+ type Output = Bytes;
+
+ #[inline]
+ fn index(&self, r: ops::RangeToInclusive<usize>) -> &Bytes {
+ Bytes::new(&self.as_bytes()[..=r.end])
+ }
+ }
+
+ impl AsRef<[u8]> for Bytes {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+ }
+
+ impl AsRef<Bytes> for [u8] {
+ #[inline]
+ fn as_ref(&self) -> &Bytes {
+ Bytes::new(self)
+ }
+ }
+
+ impl AsRef<Bytes> for str {
+ #[inline]
+ fn as_ref(&self) -> &Bytes {
+ Bytes::new(self)
+ }
+ }
+
+ #[cfg(feature = "alloc")]
+ impl crate::lib::std::borrow::ToOwned for Bytes {
+ type Owned = crate::lib::std::vec::Vec<u8>;
+
+ #[inline]
+ fn to_owned(&self) -> Self::Owned {
+ crate::lib::std::vec::Vec::from(self.as_bytes())
+ }
+ }
+
+ #[cfg(feature = "alloc")]
+ impl crate::lib::std::borrow::Borrow<Bytes> for crate::lib::std::vec::Vec<u8> {
+ #[inline]
+ fn borrow(&self) -> &Bytes {
+ Bytes::from_bytes(self.as_slice())
+ }
+ }
+
+ impl<'a> Default for &'a Bytes {
+ fn default() -> &'a Bytes {
+ Bytes::new(b"")
+ }
+ }
+
+ impl<'a> From<&'a [u8]> for &'a Bytes {
+ #[inline]
+ fn from(s: &'a [u8]) -> &'a Bytes {
+ Bytes::new(s)
+ }
+ }
+
+ impl<'a> From<&'a Bytes> for &'a [u8] {
+ #[inline]
+ fn from(s: &'a Bytes) -> &'a [u8] {
+ Bytes::as_bytes(s)
+ }
+ }
+
+ impl<'a> From<&'a str> for &'a Bytes {
+ #[inline]
+ fn from(s: &'a str) -> &'a Bytes {
+ Bytes::new(s.as_bytes())
+ }
+ }
+
+ impl Eq for Bytes {}
+
+ impl PartialEq<Bytes> for Bytes {
+ #[inline]
+ fn eq(&self, other: &Bytes) -> bool {
+ self.as_bytes() == other.as_bytes()
+ }
+ }
+
+ impl_partial_eq!(Bytes, [u8]);
+ impl_partial_eq!(Bytes, &'a [u8]);
+ impl_partial_eq!(Bytes, str);
+ impl_partial_eq!(Bytes, &'a str);
+
+ impl PartialOrd for Bytes {
+ #[inline]
+ fn partial_cmp(&self, other: &Bytes) -> Option<Ordering> {
+ 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<usize> for BStr {
+ type Output = u8;
+
+ #[inline]
+ fn index(&self, idx: usize) -> &u8 {
+ &self.as_bytes()[idx]
+ }
+ }
+
+ impl ops::Index<ops::RangeFull> for BStr {
+ type Output = BStr;
+
+ #[inline]
+ fn index(&self, _: ops::RangeFull) -> &BStr {
+ self
+ }
+ }
+
+ impl ops::Index<ops::Range<usize>> for BStr {
+ type Output = BStr;
+
+ #[inline]
+ fn index(&self, r: ops::Range<usize>) -> &BStr {
+ BStr::new(&self.as_bytes()[r.start..r.end])
+ }
+ }
+
+ impl ops::Index<ops::RangeInclusive<usize>> for BStr {
+ type Output = BStr;
+
+ #[inline]
+ fn index(&self, r: ops::RangeInclusive<usize>) -> &BStr {
+ BStr::new(&self.as_bytes()[*r.start()..=*r.end()])
+ }
+ }
+
+ impl ops::Index<ops::RangeFrom<usize>> for BStr {
+ type Output = BStr;
+
+ #[inline]
+ fn index(&self, r: ops::RangeFrom<usize>) -> &BStr {
+ BStr::new(&self.as_bytes()[r.start..])
+ }
+ }
+
+ impl ops::Index<ops::RangeTo<usize>> for BStr {
+ type Output = BStr;
+
+ #[inline]
+ fn index(&self, r: ops::RangeTo<usize>) -> &BStr {
+ BStr::new(&self.as_bytes()[..r.end])
+ }
+ }
+
+ impl ops::Index<ops::RangeToInclusive<usize>> for BStr {
+ type Output = BStr;
+
+ #[inline]
+ fn index(&self, r: ops::RangeToInclusive<usize>) -> &BStr {
+ BStr::new(&self.as_bytes()[..=r.end])
+ }
+ }
+
+ impl AsRef<[u8]> for BStr {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+ }
+
+ impl AsRef<BStr> for [u8] {
+ #[inline]
+ fn as_ref(&self) -> &BStr {
+ BStr::new(self)
+ }
+ }
+
+ impl AsRef<BStr> for str {
+ #[inline]
+ fn as_ref(&self) -> &BStr {
+ BStr::new(self)
+ }
+ }
+
+ #[cfg(feature = "alloc")]
+ impl crate::lib::std::borrow::ToOwned for BStr {
+ type Owned = crate::lib::std::vec::Vec<u8>;
+
+ #[inline]
+ fn to_owned(&self) -> Self::Owned {
+ crate::lib::std::vec::Vec::from(self.as_bytes())
+ }
+ }
+
+ #[cfg(feature = "alloc")]
+ impl crate::lib::std::borrow::Borrow<BStr> for crate::lib::std::vec::Vec<u8> {
+ #[inline]
+ fn borrow(&self) -> &BStr {
+ BStr::from_bytes(self.as_slice())
+ }
+ }
+
+ impl<'a> Default for &'a BStr {
+ fn default() -> &'a BStr {
+ BStr::new(b"")
+ }
+ }
+
+ impl<'a> From<&'a [u8]> for &'a BStr {
+ #[inline]
+ fn from(s: &'a [u8]) -> &'a BStr {
+ BStr::new(s)
+ }
+ }
+
+ impl<'a> From<&'a BStr> for &'a [u8] {
+ #[inline]
+ fn from(s: &'a BStr) -> &'a [u8] {
+ BStr::as_bytes(s)
+ }
+ }
+
+ impl<'a> From<&'a str> for &'a BStr {
+ #[inline]
+ fn from(s: &'a str) -> &'a BStr {
+ BStr::new(s.as_bytes())
+ }
+ }
+
+ impl Eq for BStr {}
+
+ impl PartialEq<BStr> for BStr {
+ #[inline]
+ fn eq(&self, other: &BStr) -> bool {
+ self.as_bytes() == other.as_bytes()
+ }
+ }
+
+ impl_partial_eq!(BStr, [u8]);
+ impl_partial_eq!(BStr, &'a [u8]);
+ impl_partial_eq!(BStr, str);
+ impl_partial_eq!(BStr, &'a str);
+
+ impl PartialOrd for BStr {
+ #[inline]
+ fn partial_cmp(&self, other: &BStr) -> Option<Ordering> {
+ 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");
+ }
+ }
+}
diff --git a/src/stream/mod.rs b/src/stream/mod.rs
new file mode 100644
index 0000000..5f2152e
--- /dev/null
+++ b/src/stream/mod.rs
@@ -0,0 +1,2727 @@
+//! Stream capability for combinators to parse
+//!
+//! Stream types include:
+//! - `&[u8]` and [`Bytes`] for binary data
+//! - `&str` (aliased as [`Str`]) and [`BStr`] for UTF-8 data
+//! - [`Located`] can track the location within the original buffer to report
+//! [spans][crate::Parser::with_span]
+//! - [`Stateful`] to thread global state through your parsers
+//! - [`Partial`] can mark an input as partial buffer that is being streamed into
+//! - [Custom stream types][crate::_topic::stream]
+
+use core::num::NonZeroUsize;
+
+use crate::error::Needed;
+use crate::lib::std::iter::{Cloned, Enumerate};
+use crate::lib::std::slice::Iter;
+use crate::lib::std::str::from_utf8;
+use crate::lib::std::str::CharIndices;
+use crate::lib::std::str::FromStr;
+
+#[allow(unused_imports)]
+#[cfg(feature = "unstable-doc")]
+use crate::error::ErrMode;
+
+#[cfg(feature = "alloc")]
+use crate::lib::std::collections::BTreeMap;
+#[cfg(feature = "std")]
+use crate::lib::std::collections::HashMap;
+#[cfg(feature = "alloc")]
+use crate::lib::std::string::String;
+#[cfg(feature = "alloc")]
+use crate::lib::std::vec::Vec;
+
+mod impls;
+#[cfg(test)]
+mod tests;
+
+/// UTF-8 Stream
+pub type Str<'i> = &'i str;
+
+/// Improved `Debug` experience for `&[u8]` byte streams
+#[allow(clippy::derive_hash_xor_eq)]
+#[derive(Hash)]
+#[repr(transparent)]
+pub struct Bytes([u8]);
+
+impl Bytes {
+ /// Make a stream out of a byte slice-like.
+ #[inline]
+ pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self {
+ Self::from_bytes(bytes.as_ref())
+ }
+
+ #[inline]
+ fn from_bytes(slice: &[u8]) -> &Self {
+ unsafe { crate::lib::std::mem::transmute(slice) }
+ }
+
+ #[inline]
+ fn as_bytes(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+/// Improved `Debug` experience for `&[u8]` UTF-8-ish streams
+#[allow(clippy::derive_hash_xor_eq)]
+#[derive(Hash)]
+#[repr(transparent)]
+pub struct BStr([u8]);
+
+impl BStr {
+ /// Make a stream out of a byte slice-like.
+ #[inline]
+ pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self {
+ Self::from_bytes(bytes.as_ref())
+ }
+
+ #[inline]
+ fn from_bytes(slice: &[u8]) -> &Self {
+ unsafe { crate::lib::std::mem::transmute(slice) }
+ }
+
+ #[inline]
+ fn as_bytes(&self) -> &[u8] {
+ &self.0
+ }
+}
+
+/// Allow collecting the span of a parsed token
+///
+/// See [`Parser::span`][crate::Parser::span] and [`Parser::with_span`][crate::Parser::with_span] for more details
+#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Located<I> {
+ initial: I,
+ input: I,
+}
+
+impl<I> Located<I>
+where
+ I: Clone + Offset,
+{
+ /// Wrap another Stream with span tracking
+ pub fn new(input: I) -> Self {
+ let initial = input.clone();
+ Self { initial, input }
+ }
+
+ fn location(&self) -> usize {
+ self.input.offset_from(&self.initial)
+ }
+}
+
+impl<I> AsRef<I> for Located<I> {
+ #[inline(always)]
+ fn as_ref(&self) -> &I {
+ &self.input
+ }
+}
+
+impl<I> crate::lib::std::ops::Deref for Located<I> {
+ type Target = I;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.input
+ }
+}
+
+impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for Located<I> {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ self.input.fmt(f)
+ }
+}
+
+/// Thread global state through your parsers
+///
+/// Use cases
+/// - Recursion checks
+/// - Error recovery
+/// - Debugging
+///
+/// # Example
+///
+/// ```
+/// # use std::cell::Cell;
+/// # use winnow::prelude::*;
+/// # use winnow::stream::Stateful;
+/// # use winnow::ascii::alpha1;
+/// # type Error = ();
+///
+/// #[derive(Clone, Debug)]
+/// struct State<'s>(&'s Cell<u32>);
+///
+/// impl<'s> State<'s> {
+/// fn count(&self) {
+/// self.0.set(self.0.get() + 1);
+/// }
+/// }
+///
+/// type Stream<'is> = Stateful<&'is str, State<'is>>;
+///
+/// fn word<'s>(i: &mut Stream<'s>) -> PResult<&'s str> {
+/// i.state.count();
+/// alpha1.parse_next(i)
+/// }
+///
+/// let data = "Hello";
+/// let state = Cell::new(0);
+/// let input = Stream { input: data, state: State(&state) };
+/// let output = word.parse(input).unwrap();
+/// assert_eq!(state.get(), 1);
+/// ```
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct Stateful<I, S> {
+ /// Inner input being wrapped in state
+ pub input: I,
+ /// User-provided state
+ pub state: S,
+}
+
+impl<I, S> AsRef<I> for Stateful<I, S> {
+ #[inline(always)]
+ fn as_ref(&self) -> &I {
+ &self.input
+ }
+}
+
+impl<I, S> crate::lib::std::ops::Deref for Stateful<I, S> {
+ type Target = I;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ self.as_ref()
+ }
+}
+
+impl<I: crate::lib::std::fmt::Display, S> crate::lib::std::fmt::Display for Stateful<I, S> {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ self.input.fmt(f)
+ }
+}
+
+/// Mark the input as a partial buffer for streaming input.
+///
+/// Complete input means that we already have all of the data. This will be the common case with
+/// small files that can be read entirely to memory.
+///
+/// In contrast, streaming input assumes that we might not have all of the data.
+/// This can happen with some network protocol or large file parsers, where the
+/// input buffer can be full and need to be resized or refilled.
+/// - [`ErrMode::Incomplete`] will report how much more data is needed.
+/// - [`Parser::complete_err`][crate::Parser::complete_err] transform [`ErrMode::Incomplete`] to
+/// [`ErrMode::Backtrack`]
+///
+/// See also [`StreamIsPartial`] to tell whether the input supports complete or partial parsing.
+///
+/// See also [Special Topics: Parsing Partial Input][crate::_topic::partial].
+///
+/// # Example
+///
+/// Here is how it works in practice:
+///
+/// ```rust
+/// # use winnow::{PResult, error::ErrMode, error::Needed, error::{InputError, ErrorKind}, token, ascii, stream::Partial};
+/// # use winnow::prelude::*;
+///
+/// fn take_partial<'s>(i: &mut Partial<&'s [u8]>) -> PResult<&'s [u8], InputError<Partial<&'s [u8]>>> {
+/// token::take(4u8).parse_next(i)
+/// }
+///
+/// fn take_complete<'s>(i: &mut &'s [u8]) -> PResult<&'s [u8], InputError<&'s [u8]>> {
+/// token::take(4u8).parse_next(i)
+/// }
+///
+/// // both parsers will take 4 bytes as expected
+/// assert_eq!(take_partial.parse_peek(Partial::new(&b"abcde"[..])), Ok((Partial::new(&b"e"[..]), &b"abcd"[..])));
+/// assert_eq!(take_complete.parse_peek(&b"abcde"[..]), Ok((&b"e"[..], &b"abcd"[..])));
+///
+/// // if the input is smaller than 4 bytes, the partial parser
+/// // will return `Incomplete` to indicate that we need more data
+/// assert_eq!(take_partial.parse_peek(Partial::new(&b"abc"[..])), Err(ErrMode::Incomplete(Needed::new(1))));
+///
+/// // but the complete parser will return an error
+/// assert_eq!(take_complete.parse_peek(&b"abc"[..]), Err(ErrMode::Backtrack(InputError::new(&b"abc"[..], ErrorKind::Slice))));
+///
+/// // the alpha0 function recognizes 0 or more alphabetic characters
+/// fn alpha0_partial<'s>(i: &mut Partial<&'s str>) -> PResult<&'s str, InputError<Partial<&'s str>>> {
+/// ascii::alpha0.parse_next(i)
+/// }
+///
+/// fn alpha0_complete<'s>(i: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// ascii::alpha0.parse_next(i)
+/// }
+///
+/// // if there's a clear limit to the recognized characters, both parsers work the same way
+/// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd;")), Ok((Partial::new(";"), "abcd")));
+/// assert_eq!(alpha0_complete.parse_peek("abcd;"), Ok((";", "abcd")));
+///
+/// // but when there's no limit, the partial version returns `Incomplete`, because it cannot
+/// // know if more input data should be recognized. The whole input could be "abcd;", or
+/// // "abcde;"
+/// assert_eq!(alpha0_partial.parse_peek(Partial::new("abcd")), Err(ErrMode::Incomplete(Needed::new(1))));
+///
+/// // while the complete version knows that all of the data is there
+/// assert_eq!(alpha0_complete.parse_peek("abcd"), Ok(("", "abcd")));
+/// ```
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Partial<I> {
+ input: I,
+ partial: bool,
+}
+
+impl<I> Partial<I>
+where
+ I: StreamIsPartial,
+{
+ /// Create a partial input
+ pub fn new(input: I) -> Self {
+ debug_assert!(
+ !I::is_partial_supported(),
+ "`Partial` can only wrap complete sources"
+ );
+ let partial = true;
+ Self { input, partial }
+ }
+
+ /// Extract the original [`Stream`]
+ #[inline(always)]
+ pub fn into_inner(self) -> I {
+ self.input
+ }
+}
+
+impl<I> Default for Partial<I>
+where
+ I: Default + StreamIsPartial,
+{
+ fn default() -> Self {
+ Self::new(I::default())
+ }
+}
+
+impl<I> crate::lib::std::ops::Deref for Partial<I> {
+ type Target = I;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.input
+ }
+}
+
+impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for Partial<I> {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ self.input.fmt(f)
+ }
+}
+
+/// Abstract method to calculate the input length
+pub trait SliceLen {
+ /// Calculates the input length, as indicated by its name,
+ /// and the name of the trait itself
+ fn slice_len(&self) -> usize;
+}
+
+impl<'a, T> SliceLen for &'a [T] {
+ #[inline]
+ fn slice_len(&self) -> usize {
+ self.len()
+ }
+}
+
+impl<T, const LEN: usize> SliceLen for [T; LEN] {
+ #[inline]
+ fn slice_len(&self) -> usize {
+ self.len()
+ }
+}
+
+impl<'a, T, const LEN: usize> SliceLen for &'a [T; LEN] {
+ #[inline]
+ fn slice_len(&self) -> usize {
+ self.len()
+ }
+}
+
+impl<'a> SliceLen for &'a str {
+ #[inline]
+ fn slice_len(&self) -> usize {
+ self.len()
+ }
+}
+
+impl<'a> SliceLen for &'a Bytes {
+ #[inline]
+ fn slice_len(&self) -> usize {
+ self.len()
+ }
+}
+
+impl<'a> SliceLen for &'a BStr {
+ #[inline]
+ fn slice_len(&self) -> usize {
+ self.len()
+ }
+}
+
+impl<I> SliceLen for (I, usize, usize)
+where
+ I: SliceLen,
+{
+ #[inline(always)]
+ fn slice_len(&self) -> usize {
+ self.0.slice_len() * 8 + self.2 - self.1
+ }
+}
+
+impl<I> SliceLen for Located<I>
+where
+ I: SliceLen,
+{
+ #[inline(always)]
+ fn slice_len(&self) -> usize {
+ self.input.slice_len()
+ }
+}
+
+impl<I, S> SliceLen for Stateful<I, S>
+where
+ I: SliceLen,
+{
+ #[inline(always)]
+ fn slice_len(&self) -> usize {
+ self.input.slice_len()
+ }
+}
+
+impl<I> SliceLen for Partial<I>
+where
+ I: SliceLen,
+{
+ #[inline(always)]
+ fn slice_len(&self) -> usize {
+ self.input.slice_len()
+ }
+}
+
+/// Core definition for parser input state
+pub trait Stream: Offset<<Self as Stream>::Checkpoint> + crate::lib::std::fmt::Debug {
+ /// The smallest unit being parsed
+ ///
+ /// Example: `u8` for `&[u8]` or `char` for `&str`
+ type Token: crate::lib::std::fmt::Debug;
+ /// Sequence of `Token`s
+ ///
+ /// Example: `&[u8]` for `Located<&[u8]>` or `&str` for `Located<&str>`
+ type Slice: crate::lib::std::fmt::Debug;
+
+ /// Iterate with the offset from the current location
+ type IterOffsets: Iterator<Item = (usize, Self::Token)>;
+
+ /// A parse location within the stream
+ type Checkpoint: Offset + Clone + crate::lib::std::fmt::Debug;
+
+ /// Iterate with the offset from the current location
+ fn iter_offsets(&self) -> Self::IterOffsets;
+ /// Returns the 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<Self::Token>;
+ /// Split off the next token from the input
+ #[inline(always)]
+ fn peek_token(&self) -> Option<(Self, Self::Token)>
+ where
+ Self: Clone,
+ {
+ let mut peek = self.clone();
+ let token = peek.next_token()?;
+ Some((peek, token))
+ }
+
+ /// Finds the offset of the next matching token
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool;
+ /// Get the offset for the number of `tokens` into the stream
+ ///
+ /// This means "0 tokens" will return `0` offset
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed>;
+ /// Split off a slice of tokens from the input
+ ///
+ /// **NOTE:** For inputs with variable width tokens, like `&str`'s `char`, `offset` might not correspond
+ /// with the number of tokens. To get a valid offset, use:
+ /// - [`Stream::eof_offset`]
+ /// - [`Stream::iter_offsets`]
+ /// - [`Stream::offset_for`]
+ /// - [`Stream::offset_at`]
+ ///
+ /// # Panic
+ ///
+ /// This will panic if
+ ///
+ /// * Indexes must be within bounds of the original input;
+ /// * Indexes must uphold invariants of the stream, like for `str` they must lie on UTF-8
+ /// sequence boundaries.
+ ///
+ fn next_slice(&mut self, offset: usize) -> Self::Slice;
+ /// Split off a slice of tokens from the input
+ #[inline(always)]
+ fn peek_slice(&self, offset: usize) -> (Self, Self::Slice)
+ where
+ Self: Clone,
+ {
+ let mut peek = self.clone();
+ let slice = peek.next_slice(offset);
+ (peek, slice)
+ }
+
+ /// Advance to the end of the stream
+ #[inline(always)]
+ fn finish(&mut self) -> Self::Slice {
+ self.next_slice(self.eof_offset())
+ }
+ /// Advance to the end of the stream
+ #[inline(always)]
+ fn peek_finish(&self) -> (Self, Self::Slice)
+ where
+ Self: Clone,
+ {
+ let mut peek = self.clone();
+ let slice = peek.finish();
+ (peek, slice)
+ }
+
+ /// Save the current parse location within the stream
+ fn checkpoint(&self) -> Self::Checkpoint;
+ /// Revert the stream to a prior [`Self::Checkpoint`]
+ ///
+ /// # Panic
+ ///
+ /// May panic if an invalid [`Self::Checkpoint`] is provided
+ fn reset(&mut self, checkpoint: Self::Checkpoint);
+
+ /// Return the inner-most stream
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug;
+}
+
+impl<'i, T> Stream for &'i [T]
+where
+ T: Clone + crate::lib::std::fmt::Debug,
+{
+ type Token = T;
+ type Slice = &'i [T];
+
+ type IterOffsets = Enumerate<Cloned<Iter<'i, T>>>;
+
+ type Checkpoint = Checkpoint<Self>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.iter().cloned().enumerate()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.len()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ let (token, next) = self.split_first()?;
+ *self = next;
+ Some(token.clone())
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.iter().position(|b| predicate(b.clone()))
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) {
+ Err(Needed::Size(needed))
+ } else {
+ Ok(tokens)
+ }
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ let (slice, next) = self.split_at(offset);
+ *self = next;
+ slice
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(*self)
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ *self = checkpoint.0;
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ self
+ }
+}
+
+impl<'i> Stream for &'i str {
+ type Token = char;
+ type Slice = &'i str;
+
+ type IterOffsets = CharIndices<'i>;
+
+ type Checkpoint = Checkpoint<Self>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.char_indices()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.len()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ let c = self.chars().next()?;
+ let offset = c.len();
+ *self = &self[offset..];
+ Some(c)
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ for (o, c) in self.iter_offsets() {
+ if predicate(c) {
+ return Some(o);
+ }
+ }
+ None
+ }
+ #[inline]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ let mut cnt = 0;
+ for (offset, _) in self.iter_offsets() {
+ if cnt == tokens {
+ return Ok(offset);
+ }
+ cnt += 1;
+ }
+
+ if cnt == tokens {
+ Ok(self.eof_offset())
+ } else {
+ Err(Needed::Unknown)
+ }
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ let (slice, next) = self.split_at(offset);
+ *self = next;
+ slice
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(*self)
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ *self = checkpoint.0;
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ self
+ }
+}
+
+impl<'i> Stream for &'i Bytes {
+ type Token = u8;
+ type Slice = &'i [u8];
+
+ type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>;
+
+ type Checkpoint = Checkpoint<Self>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.iter().cloned().enumerate()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.len()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ if self.is_empty() {
+ None
+ } else {
+ let token = self[0];
+ *self = &self[1..];
+ Some(token)
+ }
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.iter().position(|b| predicate(*b))
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) {
+ Err(Needed::Size(needed))
+ } else {
+ Ok(tokens)
+ }
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ let (slice, next) = self.0.split_at(offset);
+ *self = Bytes::from_bytes(next);
+ slice
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(*self)
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ *self = checkpoint.0;
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ self
+ }
+}
+
+impl<'i> Stream for &'i BStr {
+ type Token = u8;
+ type Slice = &'i [u8];
+
+ type IterOffsets = Enumerate<Cloned<Iter<'i, u8>>>;
+
+ type Checkpoint = Checkpoint<Self>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.iter().cloned().enumerate()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.len()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ if self.is_empty() {
+ None
+ } else {
+ let token = self[0];
+ *self = &self[1..];
+ Some(token)
+ }
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.iter().position(|b| predicate(*b))
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ if let Some(needed) = tokens.checked_sub(self.len()).and_then(NonZeroUsize::new) {
+ Err(Needed::Size(needed))
+ } else {
+ Ok(tokens)
+ }
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ let (slice, next) = self.0.split_at(offset);
+ *self = BStr::from_bytes(next);
+ slice
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(*self)
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ *self = checkpoint.0;
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ self
+ }
+}
+
+impl<I> Stream for (I, usize)
+where
+ I: Stream<Token = u8> + Clone,
+{
+ type Token = bool;
+ type Slice = (I::Slice, usize, usize);
+
+ type IterOffsets = BitOffsets<I>;
+
+ type Checkpoint = Checkpoint<(I::Checkpoint, usize)>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ BitOffsets {
+ i: self.clone(),
+ o: 0,
+ }
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ let offset = self.0.eof_offset() * 8;
+ if offset == 0 {
+ 0
+ } else {
+ offset - self.1
+ }
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ next_bit(self)
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.iter_offsets()
+ .find_map(|(o, b)| predicate(b).then_some(o))
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ if let Some(needed) = tokens
+ .checked_sub(self.eof_offset())
+ .and_then(NonZeroUsize::new)
+ {
+ Err(Needed::Size(needed))
+ } else {
+ Ok(tokens)
+ }
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ let byte_offset = (offset + self.1) / 8;
+ let end_offset = (offset + self.1) % 8;
+ let s = self.0.next_slice(byte_offset);
+ let start_offset = self.1;
+ self.1 = end_offset;
+ (s, start_offset, end_offset)
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint((self.0.checkpoint(), self.1))
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ self.0.reset(checkpoint.0 .0);
+ self.1 = checkpoint.0 .1;
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ &self.0
+ }
+}
+
+/// Iterator for [bit][crate::binary::bits] stream (`(I, usize)`)
+pub struct BitOffsets<I> {
+ i: (I, usize),
+ o: usize,
+}
+
+impl<I> Iterator for BitOffsets<I>
+where
+ I: Stream<Token = u8> + Clone,
+{
+ type Item = (usize, bool);
+ fn next(&mut self) -> Option<Self::Item> {
+ let b = next_bit(&mut self.i)?;
+ let o = self.o;
+
+ self.o += 1;
+
+ Some((o, b))
+ }
+}
+
+fn next_bit<I>(i: &mut (I, usize)) -> Option<bool>
+where
+ I: Stream<Token = u8> + Clone,
+{
+ if i.eof_offset() == 0 {
+ return None;
+ }
+ let offset = i.1;
+
+ let mut next_i = i.0.clone();
+ let byte = next_i.next_token()?;
+ let bit = (byte >> offset) & 0x1 == 0x1;
+
+ let next_offset = offset + 1;
+ if next_offset == 8 {
+ i.0 = next_i;
+ i.1 = 0;
+ Some(bit)
+ } else {
+ i.1 = next_offset;
+ Some(bit)
+ }
+}
+
+impl<I: Stream> Stream for Located<I> {
+ type Token = <I as Stream>::Token;
+ type Slice = <I as Stream>::Slice;
+
+ type IterOffsets = <I as Stream>::IterOffsets;
+
+ type Checkpoint = Checkpoint<I::Checkpoint>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.input.iter_offsets()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.input.eof_offset()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ self.input.next_token()
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.input.offset_for(predicate)
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ self.input.offset_at(tokens)
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ self.input.next_slice(offset)
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(self.input.checkpoint())
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ self.input.reset(checkpoint.0);
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ &self.input
+ }
+}
+
+impl<I: Stream, S: Clone + crate::lib::std::fmt::Debug> Stream for Stateful<I, S> {
+ type Token = <I as Stream>::Token;
+ type Slice = <I as Stream>::Slice;
+
+ type IterOffsets = <I as Stream>::IterOffsets;
+
+ type Checkpoint = Checkpoint<I::Checkpoint>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.input.iter_offsets()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.input.eof_offset()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ self.input.next_token()
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.input.offset_for(predicate)
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ self.input.offset_at(tokens)
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ self.input.next_slice(offset)
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(self.input.checkpoint())
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ self.input.reset(checkpoint.0);
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ &self.input
+ }
+}
+
+impl<I: Stream> Stream for Partial<I> {
+ type Token = <I as Stream>::Token;
+ type Slice = <I as Stream>::Slice;
+
+ type IterOffsets = <I as Stream>::IterOffsets;
+
+ type Checkpoint = Checkpoint<I::Checkpoint>;
+
+ #[inline(always)]
+ fn iter_offsets(&self) -> Self::IterOffsets {
+ self.input.iter_offsets()
+ }
+ #[inline(always)]
+ fn eof_offset(&self) -> usize {
+ self.input.eof_offset()
+ }
+
+ #[inline(always)]
+ fn next_token(&mut self) -> Option<Self::Token> {
+ self.input.next_token()
+ }
+
+ #[inline(always)]
+ fn offset_for<P>(&self, predicate: P) -> Option<usize>
+ where
+ P: Fn(Self::Token) -> bool,
+ {
+ self.input.offset_for(predicate)
+ }
+ #[inline(always)]
+ fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
+ self.input.offset_at(tokens)
+ }
+ #[inline(always)]
+ fn next_slice(&mut self, offset: usize) -> Self::Slice {
+ self.input.next_slice(offset)
+ }
+
+ #[inline(always)]
+ fn checkpoint(&self) -> Self::Checkpoint {
+ Checkpoint(self.input.checkpoint())
+ }
+ #[inline(always)]
+ fn reset(&mut self, checkpoint: Self::Checkpoint) {
+ self.input.reset(checkpoint.0);
+ }
+
+ #[inline(always)]
+ fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
+ &self.input
+ }
+}
+
+/// Number of indices input has advanced since start of parsing
+pub trait Location {
+ /// Number of indices input has advanced since start of parsing
+ fn location(&self) -> usize;
+}
+
+impl<I> Location for Located<I>
+where
+ I: Clone + Offset,
+{
+ #[inline(always)]
+ fn location(&self) -> usize {
+ self.location()
+ }
+}
+
+impl<I, S> Location for Stateful<I, S>
+where
+ I: Location,
+{
+ #[inline(always)]
+ fn location(&self) -> usize {
+ self.input.location()
+ }
+}
+
+impl<I> Location for Partial<I>
+where
+ I: Location,
+{
+ #[inline(always)]
+ fn location(&self) -> usize {
+ self.input.location()
+ }
+}
+
+/// Marks the input as being the complete buffer or a partial buffer for streaming input
+///
+/// See [`Partial`] for marking a presumed complete buffer type as a streaming buffer.
+pub trait StreamIsPartial: Sized {
+ /// Whether the stream is currently partial or complete
+ type PartialState;
+
+ /// Mark the stream is complete
+ #[must_use]
+ fn complete(&mut self) -> Self::PartialState;
+
+ /// Restore the stream back to its previous state
+ fn restore_partial(&mut self, state: Self::PartialState);
+
+ /// Report whether the [`Stream`] is can ever be incomplete
+ fn is_partial_supported() -> bool;
+
+ /// Report whether the [`Stream`] is currently incomplete
+ #[inline(always)]
+ fn is_partial(&self) -> bool {
+ Self::is_partial_supported()
+ }
+}
+
+impl<'a, T> StreamIsPartial for &'a [T] {
+ type PartialState = ();
+
+ fn complete(&mut self) -> Self::PartialState {}
+
+ fn restore_partial(&mut self, _state: Self::PartialState) {}
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ false
+ }
+}
+
+impl<'a> StreamIsPartial for &'a str {
+ type PartialState = ();
+
+ fn complete(&mut self) -> Self::PartialState {
+ // Already complete
+ }
+
+ fn restore_partial(&mut self, _state: Self::PartialState) {}
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ false
+ }
+}
+
+impl<'a> StreamIsPartial for &'a Bytes {
+ type PartialState = ();
+
+ fn complete(&mut self) -> Self::PartialState {
+ // Already complete
+ }
+
+ fn restore_partial(&mut self, _state: Self::PartialState) {}
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ false
+ }
+}
+
+impl<'a> StreamIsPartial for &'a BStr {
+ type PartialState = ();
+
+ fn complete(&mut self) -> Self::PartialState {
+ // Already complete
+ }
+
+ fn restore_partial(&mut self, _state: Self::PartialState) {}
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ false
+ }
+}
+
+impl<I> StreamIsPartial for (I, usize)
+where
+ I: StreamIsPartial,
+{
+ type PartialState = I::PartialState;
+
+ fn complete(&mut self) -> Self::PartialState {
+ self.0.complete()
+ }
+
+ fn restore_partial(&mut self, state: Self::PartialState) {
+ self.0.restore_partial(state);
+ }
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ I::is_partial_supported()
+ }
+
+ #[inline(always)]
+ fn is_partial(&self) -> bool {
+ self.0.is_partial()
+ }
+}
+
+impl<I> StreamIsPartial for Located<I>
+where
+ I: StreamIsPartial,
+{
+ type PartialState = I::PartialState;
+
+ fn complete(&mut self) -> Self::PartialState {
+ self.input.complete()
+ }
+
+ fn restore_partial(&mut self, state: Self::PartialState) {
+ self.input.restore_partial(state);
+ }
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ I::is_partial_supported()
+ }
+
+ #[inline(always)]
+ fn is_partial(&self) -> bool {
+ self.input.is_partial()
+ }
+}
+
+impl<I, S> StreamIsPartial for Stateful<I, S>
+where
+ I: StreamIsPartial,
+{
+ type PartialState = I::PartialState;
+
+ fn complete(&mut self) -> Self::PartialState {
+ self.input.complete()
+ }
+
+ fn restore_partial(&mut self, state: Self::PartialState) {
+ self.input.restore_partial(state);
+ }
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ I::is_partial_supported()
+ }
+
+ #[inline(always)]
+ fn is_partial(&self) -> bool {
+ self.input.is_partial()
+ }
+}
+
+impl<I> StreamIsPartial for Partial<I>
+where
+ I: StreamIsPartial,
+{
+ type PartialState = bool;
+
+ fn complete(&mut self) -> Self::PartialState {
+ core::mem::replace(&mut self.partial, false)
+ }
+
+ fn restore_partial(&mut self, state: Self::PartialState) {
+ self.partial = state;
+ }
+
+ #[inline(always)]
+ fn is_partial_supported() -> bool {
+ true
+ }
+
+ #[inline(always)]
+ fn is_partial(&self) -> bool {
+ self.partial
+ }
+}
+
+/// Useful functions to calculate the offset between slices and show a hexdump of a slice
+pub trait Offset<Start = Self> {
+ /// Offset between the first byte of `start` and the first byte of `self`
+ 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<I> Offset for (I, usize)
+where
+ I: Offset,
+{
+ #[inline(always)]
+ fn offset_from(&self, start: &Self) -> usize {
+ self.0.offset_from(&start.0) * 8 + self.1 - start.1
+ }
+}
+
+impl<I> Offset<<(I, usize) as Stream>::Checkpoint> for (I, usize)
+where
+ I: Stream<Token = u8> + Clone,
+{
+ #[inline(always)]
+ fn offset_from(&self, other: &<(I, usize) as Stream>::Checkpoint) -> usize {
+ self.checkpoint().offset_from(other)
+ }
+}
+
+impl<I> Offset for Located<I>
+where
+ I: Stream,
+{
+ #[inline(always)]
+ fn offset_from(&self, other: &Self) -> usize {
+ self.offset_from(&other.checkpoint())
+ }
+}
+
+impl<I> Offset<<Located<I> as Stream>::Checkpoint> for Located<I>
+where
+ I: Stream,
+{
+ #[inline(always)]
+ fn offset_from(&self, other: &<Located<I> as Stream>::Checkpoint) -> usize {
+ self.checkpoint().offset_from(other)
+ }
+}
+
+impl<I, S> Offset for Stateful<I, S>
+where
+ I: Stream,
+ S: Clone + crate::lib::std::fmt::Debug,
+{
+ #[inline(always)]
+ fn offset_from(&self, start: &Self) -> usize {
+ self.offset_from(&start.checkpoint())
+ }
+}
+
+impl<I, S> Offset<<Stateful<I, S> as Stream>::Checkpoint> for Stateful<I, S>
+where
+ I: Stream,
+ S: Clone + crate::lib::std::fmt::Debug,
+{
+ #[inline(always)]
+ fn offset_from(&self, other: &<Stateful<I, S> as Stream>::Checkpoint) -> usize {
+ self.checkpoint().offset_from(other)
+ }
+}
+
+impl<I> Offset for Partial<I>
+where
+ I: Stream,
+{
+ #[inline(always)]
+ fn offset_from(&self, start: &Self) -> usize {
+ self.offset_from(&start.checkpoint())
+ }
+}
+
+impl<I> Offset<<Partial<I> as Stream>::Checkpoint> for Partial<I>
+where
+ I: Stream,
+{
+ #[inline(always)]
+ fn offset_from(&self, other: &<Partial<I> as Stream>::Checkpoint) -> usize {
+ self.checkpoint().offset_from(other)
+ }
+}
+
+impl<I> Offset for Checkpoint<I>
+where
+ I: Offset,
+{
+ #[inline(always)]
+ fn offset_from(&self, start: &Self) -> usize {
+ self.0.offset_from(&start.0)
+ }
+}
+
+/// Helper trait for types that can be viewed as a byte slice
+pub trait AsBytes {
+ /// Casts the input type to a byte slice
+ fn as_bytes(&self) -> &[u8];
+}
+
+impl<'a> AsBytes for &'a [u8] {
+ #[inline(always)]
+ fn as_bytes(&self) -> &[u8] {
+ self
+ }
+}
+
+impl<'a> AsBytes for &'a Bytes {
+ #[inline(always)]
+ fn as_bytes(&self) -> &[u8] {
+ (*self).as_bytes()
+ }
+}
+
+impl<I> AsBytes for Located<I>
+where
+ I: AsBytes,
+{
+ #[inline(always)]
+ fn as_bytes(&self) -> &[u8] {
+ self.input.as_bytes()
+ }
+}
+
+impl<I, S> AsBytes for Stateful<I, S>
+where
+ I: AsBytes,
+{
+ #[inline(always)]
+ fn as_bytes(&self) -> &[u8] {
+ self.input.as_bytes()
+ }
+}
+
+impl<I> AsBytes for Partial<I>
+where
+ I: AsBytes,
+{
+ #[inline(always)]
+ fn as_bytes(&self) -> &[u8] {
+ self.input.as_bytes()
+ }
+}
+
+/// Helper trait for types that can be viewed as a byte slice
+pub trait AsBStr {
+ /// Casts the input type to a byte slice
+ fn as_bstr(&self) -> &[u8];
+}
+
+impl<'a> AsBStr for &'a [u8] {
+ #[inline(always)]
+ fn as_bstr(&self) -> &[u8] {
+ self
+ }
+}
+
+impl<'a> AsBStr for &'a BStr {
+ #[inline(always)]
+ fn as_bstr(&self) -> &[u8] {
+ (*self).as_bytes()
+ }
+}
+
+impl<'a> AsBStr for &'a str {
+ #[inline(always)]
+ fn as_bstr(&self) -> &[u8] {
+ (*self).as_bytes()
+ }
+}
+
+impl<I> AsBStr for Located<I>
+where
+ I: AsBStr,
+{
+ #[inline(always)]
+ fn as_bstr(&self) -> &[u8] {
+ self.input.as_bstr()
+ }
+}
+
+impl<I, S> AsBStr for Stateful<I, S>
+where
+ I: AsBStr,
+{
+ #[inline(always)]
+ fn as_bstr(&self) -> &[u8] {
+ self.input.as_bstr()
+ }
+}
+
+impl<I> AsBStr for Partial<I>
+where
+ I: AsBStr,
+{
+ #[inline(always)]
+ fn as_bstr(&self) -> &[u8] {
+ self.input.as_bstr()
+ }
+}
+
+/// Result of [`Compare::compare`]
+#[derive(Debug, Eq, PartialEq)]
+pub enum CompareResult {
+ /// Comparison was successful
+ Ok,
+ /// We need more data to be sure
+ Incomplete,
+ /// Comparison failed
+ Error,
+}
+
+/// Abstracts comparison operations
+pub trait Compare<T> {
+ /// Compares self to another value for equality
+ fn compare(&self, t: T) -> CompareResult;
+ /// Compares self to another value for equality
+ /// independently of the case.
+ ///
+ /// Warning: for `&str`, the comparison is done
+ /// by lowercasing both strings and comparing
+ /// the result. This is a temporary solution until
+ /// a better one appears
+ fn compare_no_case(&self, t: T) -> CompareResult;
+}
+
+fn lowercase_byte(c: u8) -> u8 {
+ match c {
+ b'A'..=b'Z' => c - b'A' + b'a',
+ _ => c,
+ }
+}
+
+impl<'a, 'b> Compare<&'b [u8]> for &'a [u8] {
+ #[inline]
+ fn compare(&self, t: &'b [u8]) -> CompareResult {
+ let pos = self.iter().zip(t.iter()).position(|(a, b)| a != b);
+
+ match pos {
+ Some(_) => CompareResult::Error,
+ None => {
+ if self.len() >= t.len() {
+ CompareResult::Ok
+ } else {
+ CompareResult::Incomplete
+ }
+ }
+ }
+ }
+
+ #[inline]
+ fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
+ if self
+ .iter()
+ .zip(t)
+ .any(|(a, b)| lowercase_byte(*a) != lowercase_byte(*b))
+ {
+ CompareResult::Error
+ } else if self.len() < t.len() {
+ CompareResult::Incomplete
+ } else {
+ CompareResult::Ok
+ }
+ }
+}
+
+impl<'a, const LEN: usize> Compare<[u8; LEN]> for &'a [u8] {
+ #[inline(always)]
+ fn compare(&self, t: [u8; LEN]) -> CompareResult {
+ self.compare(&t[..])
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, t: [u8; LEN]) -> CompareResult {
+ self.compare_no_case(&t[..])
+ }
+}
+
+impl<'a, 'b, const LEN: usize> Compare<&'b [u8; LEN]> for &'a [u8] {
+ #[inline(always)]
+ fn compare(&self, t: &'b [u8; LEN]) -> CompareResult {
+ self.compare(&t[..])
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, t: &'b [u8; LEN]) -> CompareResult {
+ self.compare_no_case(&t[..])
+ }
+}
+
+impl<'a, 'b> Compare<&'b str> for &'a [u8] {
+ #[inline(always)]
+ fn compare(&self, t: &'b str) -> CompareResult {
+ self.compare(t.as_bytes())
+ }
+ #[inline(always)]
+ fn compare_no_case(&self, t: &'b str) -> CompareResult {
+ self.compare_no_case(t.as_bytes())
+ }
+}
+
+impl<'a, 'b> Compare<&'b str> for &'a str {
+ #[inline(always)]
+ fn compare(&self, t: &'b str) -> CompareResult {
+ self.as_bytes().compare(t.as_bytes())
+ }
+
+ //FIXME: this version is too simple and does not use the current locale
+ #[inline]
+ fn compare_no_case(&self, t: &'b str) -> CompareResult {
+ let pos = self
+ .chars()
+ .zip(t.chars())
+ .position(|(a, b)| a.to_lowercase().ne(b.to_lowercase()));
+
+ match pos {
+ Some(_) => CompareResult::Error,
+ None => {
+ if self.len() >= t.len() {
+ CompareResult::Ok
+ } else {
+ CompareResult::Incomplete
+ }
+ }
+ }
+ }
+}
+
+impl<'a, 'b> Compare<&'b [u8]> for &'a str {
+ #[inline(always)]
+ fn compare(&self, t: &'b [u8]) -> CompareResult {
+ AsBStr::as_bstr(self).compare(t)
+ }
+ #[inline(always)]
+ fn compare_no_case(&self, t: &'b [u8]) -> CompareResult {
+ AsBStr::as_bstr(self).compare_no_case(t)
+ }
+}
+
+impl<'a, T> Compare<T> for &'a Bytes
+where
+ &'a [u8]: Compare<T>,
+{
+ #[inline(always)]
+ fn compare(&self, t: T) -> CompareResult {
+ let bytes = (*self).as_bytes();
+ bytes.compare(t)
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, t: T) -> CompareResult {
+ let bytes = (*self).as_bytes();
+ bytes.compare_no_case(t)
+ }
+}
+
+impl<'a, T> Compare<T> for &'a BStr
+where
+ &'a [u8]: Compare<T>,
+{
+ #[inline(always)]
+ fn compare(&self, t: T) -> CompareResult {
+ let bytes = (*self).as_bytes();
+ bytes.compare(t)
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, t: T) -> CompareResult {
+ let bytes = (*self).as_bytes();
+ bytes.compare_no_case(t)
+ }
+}
+
+impl<I, U> Compare<U> for Located<I>
+where
+ I: Compare<U>,
+{
+ #[inline(always)]
+ fn compare(&self, other: U) -> CompareResult {
+ self.input.compare(other)
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, other: U) -> CompareResult {
+ self.input.compare_no_case(other)
+ }
+}
+
+impl<I, S, U> Compare<U> for Stateful<I, S>
+where
+ I: Compare<U>,
+{
+ #[inline(always)]
+ fn compare(&self, other: U) -> CompareResult {
+ self.input.compare(other)
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, other: U) -> CompareResult {
+ self.input.compare_no_case(other)
+ }
+}
+
+impl<I, T> Compare<T> for Partial<I>
+where
+ I: Compare<T>,
+{
+ #[inline(always)]
+ fn compare(&self, t: T) -> CompareResult {
+ self.input.compare(t)
+ }
+
+ #[inline(always)]
+ fn compare_no_case(&self, t: T) -> CompareResult {
+ self.input.compare_no_case(t)
+ }
+}
+
+/// Look for a slice in self
+pub trait FindSlice<T> {
+ /// Returns the offset of the slice if it is found
+ fn find_slice(&self, substr: T) -> Option<usize>;
+}
+
+impl<'i, 's> FindSlice<&'s [u8]> for &'i [u8] {
+ #[inline(always)]
+ fn find_slice(&self, substr: &'s [u8]) -> Option<usize> {
+ memmem(self, substr)
+ }
+}
+
+impl<'i> FindSlice<u8> for &'i [u8] {
+ #[inline(always)]
+ fn find_slice(&self, substr: u8) -> Option<usize> {
+ memchr(substr, self)
+ }
+}
+
+impl<'i, 's> FindSlice<&'s str> for &'i [u8] {
+ #[inline(always)]
+ fn find_slice(&self, substr: &'s str) -> Option<usize> {
+ self.find_slice(substr.as_bytes())
+ }
+}
+
+impl<'i, 's> FindSlice<&'s str> for &'i str {
+ #[inline(always)]
+ fn find_slice(&self, substr: &'s str) -> Option<usize> {
+ self.find(substr)
+ }
+}
+
+impl<'i> FindSlice<char> for &'i str {
+ #[inline(always)]
+ fn find_slice(&self, substr: char) -> Option<usize> {
+ self.find(substr)
+ }
+}
+
+impl<'i, S> FindSlice<S> for &'i Bytes
+where
+ &'i [u8]: FindSlice<S>,
+{
+ #[inline(always)]
+ fn find_slice(&self, substr: S) -> Option<usize> {
+ let bytes = (*self).as_bytes();
+ let offset = bytes.find_slice(substr);
+ offset
+ }
+}
+
+impl<'i, S> FindSlice<S> for &'i BStr
+where
+ &'i [u8]: FindSlice<S>,
+{
+ #[inline(always)]
+ fn find_slice(&self, substr: S) -> Option<usize> {
+ let bytes = (*self).as_bytes();
+ let offset = bytes.find_slice(substr);
+ offset
+ }
+}
+
+impl<I, T> FindSlice<T> for Located<I>
+where
+ I: FindSlice<T>,
+{
+ #[inline(always)]
+ fn find_slice(&self, substr: T) -> Option<usize> {
+ self.input.find_slice(substr)
+ }
+}
+
+impl<I, S, T> FindSlice<T> for Stateful<I, S>
+where
+ I: FindSlice<T>,
+{
+ #[inline(always)]
+ fn find_slice(&self, substr: T) -> Option<usize> {
+ self.input.find_slice(substr)
+ }
+}
+
+impl<I, T> FindSlice<T> for Partial<I>
+where
+ I: FindSlice<T>,
+{
+ #[inline(always)]
+ fn find_slice(&self, substr: T) -> Option<usize> {
+ self.input.find_slice(substr)
+ }
+}
+
+/// Used to integrate `str`'s `parse()` method
+pub trait ParseSlice<R> {
+ /// Succeeds if `parse()` succeededThe
+ ///
+ /// The byte slice implementation will first convert it to a `&str`, then apply the `parse()`
+ /// function
+ fn parse_slice(&self) -> Option<R>;
+}
+
+impl<'a, R: FromStr> ParseSlice<R> for &'a [u8] {
+ #[inline(always)]
+ fn parse_slice(&self) -> Option<R> {
+ from_utf8(self).ok().and_then(|s| s.parse().ok())
+ }
+}
+
+impl<'a, R: FromStr> ParseSlice<R> for &'a str {
+ #[inline(always)]
+ fn parse_slice(&self) -> Option<R> {
+ self.parse().ok()
+ }
+}
+
+/// Convert a `Stream` into an appropriate `Output` type
+pub trait UpdateSlice: Stream {
+ /// Convert an `Output` type to be used as `Stream`
+ fn update_slice(self, inner: Self::Slice) -> Self;
+}
+
+impl<'a, T> UpdateSlice for &'a [T]
+where
+ T: Clone + crate::lib::std::fmt::Debug,
+{
+ #[inline(always)]
+ fn update_slice(self, inner: Self::Slice) -> Self {
+ inner
+ }
+}
+
+impl<'a> UpdateSlice for &'a str {
+ #[inline(always)]
+ fn update_slice(self, inner: Self::Slice) -> Self {
+ inner
+ }
+}
+
+impl<'a> UpdateSlice for &'a Bytes {
+ #[inline(always)]
+ fn update_slice(self, inner: Self::Slice) -> Self {
+ Bytes::new(inner)
+ }
+}
+
+impl<'a> UpdateSlice for &'a BStr {
+ #[inline(always)]
+ fn update_slice(self, inner: Self::Slice) -> Self {
+ BStr::new(inner)
+ }
+}
+
+impl<I> UpdateSlice for Located<I>
+where
+ I: UpdateSlice,
+{
+ #[inline(always)]
+ fn update_slice(mut self, inner: Self::Slice) -> Self {
+ self.input = I::update_slice(self.input, inner);
+ self
+ }
+}
+
+impl<I, S> UpdateSlice for Stateful<I, S>
+where
+ I: UpdateSlice,
+ S: Clone + crate::lib::std::fmt::Debug,
+{
+ #[inline(always)]
+ fn update_slice(mut self, inner: Self::Slice) -> Self {
+ self.input = I::update_slice(self.input, inner);
+ self
+ }
+}
+
+impl<I> UpdateSlice for Partial<I>
+where
+ I: UpdateSlice,
+{
+ #[inline(always)]
+ fn update_slice(self, inner: Self::Slice) -> Self {
+ Partial {
+ input: I::update_slice(self.input, inner),
+ partial: self.partial,
+ }
+ }
+}
+
+/// Ensure checkpoint details are kept privazte
+#[derive(Copy, Clone, Debug)]
+pub struct Checkpoint<T>(T);
+
+/// A range bounded inclusively for counting parses performed
+#[derive(PartialEq, Eq)]
+pub struct Range {
+ pub(crate) start_inclusive: usize,
+ pub(crate) end_inclusive: Option<usize>,
+}
+
+impl Range {
+ #[inline(always)]
+ fn raw(start_inclusive: usize, end_inclusive: Option<usize>) -> Self {
+ Self {
+ start_inclusive,
+ end_inclusive,
+ }
+ }
+}
+
+impl crate::lib::std::ops::RangeBounds<usize> for Range {
+ #[inline(always)]
+ fn start_bound(&self) -> crate::lib::std::ops::Bound<&usize> {
+ crate::lib::std::ops::Bound::Included(&self.start_inclusive)
+ }
+
+ #[inline(always)]
+ fn end_bound(&self) -> crate::lib::std::ops::Bound<&usize> {
+ if let Some(end_inclusive) = &self.end_inclusive {
+ crate::lib::std::ops::Bound::Included(end_inclusive)
+ } else {
+ crate::lib::std::ops::Bound::Unbounded
+ }
+ }
+}
+
+impl From<usize> for Range {
+ #[inline(always)]
+ fn from(fixed: usize) -> Self {
+ (fixed..=fixed).into()
+ }
+}
+
+impl From<crate::lib::std::ops::Range<usize>> for Range {
+ #[inline(always)]
+ fn from(range: crate::lib::std::ops::Range<usize>) -> Self {
+ let start_inclusive = range.start;
+ let end_inclusive = Some(range.end.saturating_sub(1));
+ Self::raw(start_inclusive, end_inclusive)
+ }
+}
+
+impl From<crate::lib::std::ops::RangeFull> for Range {
+ #[inline(always)]
+ fn from(_: crate::lib::std::ops::RangeFull) -> Self {
+ let start_inclusive = 0;
+ let end_inclusive = None;
+ Self::raw(start_inclusive, end_inclusive)
+ }
+}
+
+impl From<crate::lib::std::ops::RangeFrom<usize>> for Range {
+ #[inline(always)]
+ fn from(range: crate::lib::std::ops::RangeFrom<usize>) -> Self {
+ let start_inclusive = range.start;
+ let end_inclusive = None;
+ Self::raw(start_inclusive, end_inclusive)
+ }
+}
+
+impl From<crate::lib::std::ops::RangeTo<usize>> for Range {
+ #[inline(always)]
+ fn from(range: crate::lib::std::ops::RangeTo<usize>) -> Self {
+ let start_inclusive = 0;
+ let end_inclusive = Some(range.end.saturating_sub(1));
+ Self::raw(start_inclusive, end_inclusive)
+ }
+}
+
+impl From<crate::lib::std::ops::RangeInclusive<usize>> for Range {
+ #[inline(always)]
+ fn from(range: crate::lib::std::ops::RangeInclusive<usize>) -> Self {
+ let start_inclusive = *range.start();
+ let end_inclusive = Some(*range.end());
+ Self::raw(start_inclusive, end_inclusive)
+ }
+}
+
+impl From<crate::lib::std::ops::RangeToInclusive<usize>> for Range {
+ #[inline(always)]
+ fn from(range: crate::lib::std::ops::RangeToInclusive<usize>) -> Self {
+ let start_inclusive = 0;
+ let end_inclusive = Some(range.end);
+ Self::raw(start_inclusive, end_inclusive)
+ }
+}
+
+impl crate::lib::std::fmt::Display for Range {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ self.start_inclusive.fmt(f)?;
+ match self.end_inclusive {
+ Some(e) if e == self.start_inclusive => {}
+ Some(e) => {
+ "..=".fmt(f)?;
+ e.fmt(f)?;
+ }
+ None => {
+ "..".fmt(f)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl crate::lib::std::fmt::Debug for Range {
+ fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
+ write!(f, "{self}")
+ }
+}
+
+/// Abstracts something which can extend an `Extend`.
+/// Used to build modified input slices in `escaped_transform`
+pub trait Accumulate<T>: Sized {
+ /// Create a new `Extend` of the correct type
+ fn initial(capacity: Option<usize>) -> Self;
+ /// Accumulate the input into an accumulator
+ fn accumulate(&mut self, acc: T);
+}
+
+impl<T> Accumulate<T> for () {
+ #[inline(always)]
+ fn initial(_capacity: Option<usize>) -> Self {}
+ #[inline(always)]
+ fn accumulate(&mut self, _acc: T) {}
+}
+
+impl<T> Accumulate<T> for usize {
+ #[inline(always)]
+ fn initial(_capacity: Option<usize>) -> Self {
+ 0
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, _acc: T) {
+ *self += 1;
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<T> Accumulate<T> for Vec<T> {
+ #[inline(always)]
+ fn initial(capacity: Option<usize>) -> Self {
+ match capacity {
+ Some(capacity) => Vec::with_capacity(clamp_capacity::<T>(capacity)),
+ None => Vec::new(),
+ }
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, acc: T) {
+ self.push(acc);
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'i, T: Clone> Accumulate<&'i [T]> for Vec<T> {
+ #[inline(always)]
+ fn initial(capacity: Option<usize>) -> Self {
+ match capacity {
+ Some(capacity) => Vec::with_capacity(clamp_capacity::<T>(capacity)),
+ None => Vec::new(),
+ }
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, acc: &'i [T]) {
+ self.extend(acc.iter().cloned());
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl Accumulate<char> for String {
+ #[inline(always)]
+ fn initial(capacity: Option<usize>) -> Self {
+ match capacity {
+ Some(capacity) => String::with_capacity(clamp_capacity::<char>(capacity)),
+ None => String::new(),
+ }
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, acc: char) {
+ self.push(acc);
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'i> Accumulate<&'i str> for String {
+ #[inline(always)]
+ fn initial(capacity: Option<usize>) -> Self {
+ match capacity {
+ Some(capacity) => String::with_capacity(clamp_capacity::<char>(capacity)),
+ None => String::new(),
+ }
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, acc: &'i str) {
+ self.push_str(acc);
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<K, V> Accumulate<(K, V)> for BTreeMap<K, V>
+where
+ K: crate::lib::std::cmp::Ord,
+{
+ #[inline(always)]
+ fn initial(_capacity: Option<usize>) -> Self {
+ BTreeMap::new()
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, (key, value): (K, V)) {
+ self.insert(key, value);
+ }
+}
+
+#[cfg(feature = "std")]
+impl<K, V> Accumulate<(K, V)> for HashMap<K, V>
+where
+ K: crate::lib::std::cmp::Eq + crate::lib::std::hash::Hash,
+{
+ #[inline(always)]
+ fn initial(capacity: Option<usize>) -> Self {
+ match capacity {
+ Some(capacity) => HashMap::with_capacity(clamp_capacity::<(K, V)>(capacity)),
+ None => HashMap::new(),
+ }
+ }
+ #[inline(always)]
+ fn accumulate(&mut self, (key, value): (K, V)) {
+ self.insert(key, value);
+ }
+}
+
+#[cfg(feature = "alloc")]
+#[inline]
+pub(crate) fn clamp_capacity<T>(capacity: usize) -> usize {
+ /// Don't pre-allocate more than 64KiB when calling `Vec::with_capacity`.
+ ///
+ /// Pre-allocating memory is a nice optimization but count fields can't
+ /// always be trusted. We should clamp initial capacities to some reasonable
+ /// amount. This reduces the risk of a bogus count value triggering a panic
+ /// due to an OOM error.
+ ///
+ /// This does not affect correctness. `winnow` will always read the full number
+ /// of elements regardless of the capacity cap.
+ const MAX_INITIAL_CAPACITY_BYTES: usize = 65536;
+
+ let max_initial_capacity =
+ MAX_INITIAL_CAPACITY_BYTES / crate::lib::std::mem::size_of::<T>().max(1);
+ capacity.min(max_initial_capacity)
+}
+
+/// Helper trait to convert numbers to usize.
+///
+/// By default, usize implements `From<u8>` and `From<u16>` but not
+/// `From<u32>` and `From<u64>` because that would be invalid on some
+/// platforms. This trait implements the conversion for platforms
+/// with 32 and 64 bits pointer platforms
+pub trait ToUsize {
+ /// converts self to usize
+ fn to_usize(&self) -> usize;
+}
+
+impl ToUsize for u8 {
+ #[inline(always)]
+ fn to_usize(&self) -> usize {
+ *self as usize
+ }
+}
+
+impl ToUsize for u16 {
+ #[inline(always)]
+ fn to_usize(&self) -> usize {
+ *self as usize
+ }
+}
+
+impl ToUsize for usize {
+ #[inline(always)]
+ fn to_usize(&self) -> usize {
+ *self
+ }
+}
+
+#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
+impl ToUsize for u32 {
+ #[inline(always)]
+ fn to_usize(&self) -> usize {
+ *self as usize
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+impl ToUsize for u64 {
+ #[inline(always)]
+ fn to_usize(&self) -> usize {
+ *self as usize
+ }
+}
+
+/// Transforms a token into a char for basic string parsing
+#[allow(clippy::len_without_is_empty)]
+#[allow(clippy::wrong_self_convention)]
+pub trait AsChar {
+ /// Makes a char from self
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use winnow::stream::AsChar as _;
+ ///
+ /// assert_eq!('a'.as_char(), 'a');
+ /// assert_eq!(u8::MAX.as_char(), std::char::from_u32(u8::MAX as u32).unwrap());
+ /// ```
+ fn as_char(self) -> char;
+
+ /// Tests that self is an alphabetic character
+ ///
+ /// **Warning:** for `&str` it recognizes alphabetic
+ /// characters outside of the 52 ASCII letters
+ fn is_alpha(self) -> bool;
+
+ /// Tests that self is an alphabetic character
+ /// or a decimal digit
+ fn is_alphanum(self) -> bool;
+ /// Tests that self is a decimal digit
+ fn is_dec_digit(self) -> bool;
+ /// Tests that self is an hex digit
+ fn is_hex_digit(self) -> bool;
+ /// Tests that self is an octal digit
+ fn is_oct_digit(self) -> bool;
+ /// Gets the len in bytes for self
+ fn len(self) -> usize;
+ /// Tests that self is ASCII space or tab
+ fn is_space(self) -> bool;
+ /// Tests if byte is ASCII newline: \n
+ fn is_newline(self) -> bool;
+}
+
+impl AsChar for u8 {
+ #[inline(always)]
+ fn as_char(self) -> char {
+ self as char
+ }
+ #[inline]
+ fn is_alpha(self) -> bool {
+ matches!(self, 0x41..=0x5A | 0x61..=0x7A)
+ }
+ #[inline]
+ fn is_alphanum(self) -> bool {
+ self.is_alpha() || self.is_dec_digit()
+ }
+ #[inline]
+ fn is_dec_digit(self) -> bool {
+ matches!(self, 0x30..=0x39)
+ }
+ #[inline]
+ fn is_hex_digit(self) -> bool {
+ matches!(self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66)
+ }
+ #[inline]
+ fn is_oct_digit(self) -> bool {
+ matches!(self, 0x30..=0x37)
+ }
+ #[inline]
+ fn len(self) -> usize {
+ 1
+ }
+ #[inline]
+ fn is_space(self) -> bool {
+ self == b' ' || self == b'\t'
+ }
+ #[inline]
+ fn is_newline(self) -> bool {
+ self == b'\n'
+ }
+}
+impl<'a> AsChar for &'a u8 {
+ #[inline(always)]
+ fn as_char(self) -> char {
+ *self as char
+ }
+ #[inline]
+ fn is_alpha(self) -> bool {
+ matches!(*self, 0x41..=0x5A | 0x61..=0x7A)
+ }
+ #[inline]
+ fn is_alphanum(self) -> bool {
+ self.is_alpha() || self.is_dec_digit()
+ }
+ #[inline]
+ fn is_dec_digit(self) -> bool {
+ matches!(*self, 0x30..=0x39)
+ }
+ #[inline]
+ fn is_hex_digit(self) -> bool {
+ matches!(*self, 0x30..=0x39 | 0x41..=0x46 | 0x61..=0x66)
+ }
+ #[inline]
+ fn is_oct_digit(self) -> bool {
+ matches!(*self, 0x30..=0x37)
+ }
+ #[inline]
+ fn len(self) -> usize {
+ 1
+ }
+ #[inline]
+ fn is_space(self) -> bool {
+ *self == b' ' || *self == b'\t'
+ }
+ #[inline]
+ fn is_newline(self) -> bool {
+ *self == b'\n'
+ }
+}
+
+impl AsChar for char {
+ #[inline(always)]
+ fn as_char(self) -> char {
+ self
+ }
+ #[inline]
+ fn is_alpha(self) -> bool {
+ self.is_ascii_alphabetic()
+ }
+ #[inline]
+ fn is_alphanum(self) -> bool {
+ self.is_alpha() || self.is_dec_digit()
+ }
+ #[inline]
+ fn is_dec_digit(self) -> bool {
+ self.is_ascii_digit()
+ }
+ #[inline]
+ fn is_hex_digit(self) -> bool {
+ self.is_ascii_hexdigit()
+ }
+ #[inline]
+ fn is_oct_digit(self) -> bool {
+ self.is_digit(8)
+ }
+ #[inline]
+ fn len(self) -> usize {
+ self.len_utf8()
+ }
+ #[inline]
+ fn is_space(self) -> bool {
+ self == ' ' || self == '\t'
+ }
+ #[inline]
+ fn is_newline(self) -> bool {
+ self == '\n'
+ }
+}
+
+impl<'a> AsChar for &'a char {
+ #[inline(always)]
+ fn as_char(self) -> char {
+ *self
+ }
+ #[inline]
+ fn is_alpha(self) -> bool {
+ self.is_ascii_alphabetic()
+ }
+ #[inline]
+ fn is_alphanum(self) -> bool {
+ self.is_alpha() || self.is_dec_digit()
+ }
+ #[inline]
+ fn is_dec_digit(self) -> bool {
+ self.is_ascii_digit()
+ }
+ #[inline]
+ fn is_hex_digit(self) -> bool {
+ self.is_ascii_hexdigit()
+ }
+ #[inline]
+ fn is_oct_digit(self) -> bool {
+ self.is_digit(8)
+ }
+ #[inline]
+ fn len(self) -> usize {
+ self.len_utf8()
+ }
+ #[inline]
+ fn is_space(self) -> bool {
+ *self == ' ' || *self == '\t'
+ }
+ #[inline]
+ fn is_newline(self) -> bool {
+ *self == '\n'
+ }
+}
+
+/// Check if a token in in a set of possible tokens
+///
+/// This is generally implemented on patterns that a token may match and supports `u8` and `char`
+/// tokens along with the following patterns
+/// - `b'c'` and `'c'`
+/// - `b""` and `""`
+/// - `|c| true`
+/// - `b'a'..=b'z'`, `'a'..='z'` (etc for each [range type][std::ops])
+/// - `(pattern1, pattern2, ...)`
+///
+/// # Example
+///
+/// For example, you could implement `hex_digit0` as:
+/// ```
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::token::take_while;
+/// fn hex_digit1<'s>(input: &mut &'s str) -> PResult<&'s str, InputError<&'s str>> {
+/// take_while(1.., ('a'..='f', 'A'..='F', '0'..='9')).parse_next(input)
+/// }
+///
+/// assert_eq!(hex_digit1.parse_peek("21cZ"), Ok(("Z", "21c")));
+/// assert_eq!(hex_digit1.parse_peek("H2"), Err(ErrMode::Backtrack(InputError::new("H2", ErrorKind::Slice))));
+/// assert_eq!(hex_digit1.parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+pub trait ContainsToken<T> {
+ /// Returns true if self contains the token
+ fn contains_token(&self, token: T) -> bool;
+}
+
+impl ContainsToken<u8> for u8 {
+ #[inline(always)]
+ fn contains_token(&self, token: u8) -> bool {
+ *self == token
+ }
+}
+
+impl<'a> ContainsToken<&'a u8> for u8 {
+ #[inline(always)]
+ fn contains_token(&self, token: &u8) -> bool {
+ self.contains_token(*token)
+ }
+}
+
+impl ContainsToken<char> for u8 {
+ #[inline(always)]
+ fn contains_token(&self, token: char) -> bool {
+ self.as_char() == token
+ }
+}
+
+impl<'a> ContainsToken<&'a char> for u8 {
+ #[inline(always)]
+ fn contains_token(&self, token: &char) -> bool {
+ self.contains_token(*token)
+ }
+}
+
+impl<C: AsChar> ContainsToken<C> for char {
+ #[inline(always)]
+ fn contains_token(&self, token: C) -> bool {
+ *self == token.as_char()
+ }
+}
+
+impl<C: AsChar, F: Fn(C) -> bool> ContainsToken<C> for F {
+ #[inline(always)]
+ fn contains_token(&self, token: C) -> bool {
+ self(token)
+ }
+}
+
+impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> for crate::lib::std::ops::Range<C2> {
+ #[inline(always)]
+ fn contains_token(&self, token: C1) -> bool {
+ let start = self.start.clone().as_char();
+ let end = self.end.clone().as_char();
+ (start..end).contains(&token.as_char())
+ }
+}
+
+impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1>
+ for crate::lib::std::ops::RangeInclusive<C2>
+{
+ #[inline(always)]
+ fn contains_token(&self, token: C1) -> bool {
+ let start = self.start().clone().as_char();
+ let end = self.end().clone().as_char();
+ (start..=end).contains(&token.as_char())
+ }
+}
+
+impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> for crate::lib::std::ops::RangeFrom<C2> {
+ #[inline(always)]
+ fn contains_token(&self, token: C1) -> bool {
+ let start = self.start.clone().as_char();
+ (start..).contains(&token.as_char())
+ }
+}
+
+impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1> for crate::lib::std::ops::RangeTo<C2> {
+ #[inline(always)]
+ fn contains_token(&self, token: C1) -> bool {
+ let end = self.end.clone().as_char();
+ (..end).contains(&token.as_char())
+ }
+}
+
+impl<C1: AsChar, C2: AsChar + Clone> ContainsToken<C1>
+ for crate::lib::std::ops::RangeToInclusive<C2>
+{
+ #[inline(always)]
+ fn contains_token(&self, token: C1) -> bool {
+ let end = self.end.clone().as_char();
+ (..=end).contains(&token.as_char())
+ }
+}
+
+impl<C1: AsChar> ContainsToken<C1> for crate::lib::std::ops::RangeFull {
+ #[inline(always)]
+ fn contains_token(&self, _token: C1) -> bool {
+ true
+ }
+}
+
+impl<C: AsChar> ContainsToken<C> for &'_ [u8] {
+ #[inline]
+ fn contains_token(&self, token: C) -> bool {
+ let token = token.as_char();
+ self.iter().any(|t| t.as_char() == token)
+ }
+}
+
+impl<C: AsChar> ContainsToken<C> for &'_ [char] {
+ #[inline]
+ fn contains_token(&self, token: C) -> bool {
+ let token = token.as_char();
+ self.iter().any(|t| *t == token)
+ }
+}
+
+impl<const LEN: usize, C: AsChar> ContainsToken<C> for &'_ [u8; LEN] {
+ #[inline]
+ fn contains_token(&self, token: C) -> bool {
+ let token = token.as_char();
+ self.iter().any(|t| t.as_char() == token)
+ }
+}
+
+impl<const LEN: usize, C: AsChar> ContainsToken<C> for &'_ [char; LEN] {
+ #[inline]
+ fn contains_token(&self, token: C) -> bool {
+ let token = token.as_char();
+ self.iter().any(|t| *t == token)
+ }
+}
+
+impl<const LEN: usize, C: AsChar> ContainsToken<C> for [u8; LEN] {
+ #[inline]
+ fn contains_token(&self, token: C) -> bool {
+ let token = token.as_char();
+ self.iter().any(|t| t.as_char() == token)
+ }
+}
+
+impl<const LEN: usize, C: AsChar> ContainsToken<C> for [char; LEN] {
+ #[inline]
+ fn contains_token(&self, token: C) -> bool {
+ let token = token.as_char();
+ self.iter().any(|t| *t == token)
+ }
+}
+
+impl<T> ContainsToken<T> for () {
+ #[inline(always)]
+ fn contains_token(&self, _token: T) -> bool {
+ false
+ }
+}
+
+macro_rules! impl_contains_token_for_tuple {
+ ($($haystack:ident),+) => (
+ #[allow(non_snake_case)]
+ impl<T, $($haystack),+> ContainsToken<T> for ($($haystack),+,)
+ where
+ T: Clone,
+ $($haystack: ContainsToken<T>),+
+ {
+ #[inline]
+ fn contains_token(&self, token: T) -> bool {
+ let ($(ref $haystack),+,) = *self;
+ $($haystack.contains_token(token.clone()) || )+ false
+ }
+ }
+ )
+}
+
+macro_rules! impl_contains_token_for_tuples {
+ ($haystack1:ident, $($haystack:ident),+) => {
+ impl_contains_token_for_tuples!(__impl $haystack1; $($haystack),+);
+ };
+ (__impl $($haystack:ident),+; $haystack1:ident $(,$haystack2:ident)*) => {
+ impl_contains_token_for_tuple!($($haystack),+);
+ impl_contains_token_for_tuples!(__impl $($haystack),+, $haystack1; $($haystack2),*);
+ };
+ (__impl $($haystack:ident),+;) => {
+ impl_contains_token_for_tuple!($($haystack),+);
+ }
+}
+
+impl_contains_token_for_tuples!(
+ F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21
+);
+
+#[cfg(feature = "simd")]
+#[inline(always)]
+fn memchr(token: u8, slice: &[u8]) -> Option<usize> {
+ memchr::memchr(token, slice)
+}
+
+#[cfg(not(feature = "simd"))]
+#[inline(always)]
+fn memchr(token: u8, slice: &[u8]) -> Option<usize> {
+ slice.iter().position(|t| *t == token)
+}
+
+#[cfg(feature = "simd")]
+#[inline(always)]
+fn memmem(slice: &[u8], tag: &[u8]) -> Option<usize> {
+ if tag.len() > slice.len() {
+ return None;
+ }
+
+ let (&substr_first, substr_rest) = match tag.split_first() {
+ Some(split) => split,
+ // an empty substring is found at position 0
+ // This matches the behavior of str.find("").
+ None => return Some(0),
+ };
+
+ if substr_rest.is_empty() {
+ return memchr::memchr(substr_first, slice);
+ }
+
+ let mut offset = 0;
+ let haystack = &slice[..slice.len() - substr_rest.len()];
+
+ while let Some(position) = memchr::memchr(substr_first, &haystack[offset..]) {
+ offset += position;
+ let next_offset = offset + 1;
+ if &slice[next_offset..][..substr_rest.len()] == substr_rest {
+ return Some(offset);
+ }
+
+ offset = next_offset;
+ }
+
+ None
+}
+
+#[cfg(not(feature = "simd"))]
+fn memmem(slice: &[u8], tag: &[u8]) -> Option<usize> {
+ for i in 0..slice.len() {
+ let subslice = &slice[i..];
+ if subslice.starts_with(tag) {
+ return Some(i);
+ }
+ }
+ None
+}
diff --git a/src/stream/tests.rs b/src/stream/tests.rs
new file mode 100644
index 0000000..e653ad9
--- /dev/null
+++ b/src/stream/tests.rs
@@ -0,0 +1,116 @@
+#[cfg(feature = "std")]
+use proptest::prelude::*;
+
+use super::*;
+
+#[test]
+fn test_offset_u8() {
+ let s = b"abcd123";
+ let a = &s[..];
+ let b = &a[2..];
+ let c = &a[..4];
+ let d = &a[3..5];
+ assert_eq!(b.offset_from(&a), 2);
+ assert_eq!(c.offset_from(&a), 0);
+ assert_eq!(d.offset_from(&a), 3);
+}
+
+#[test]
+fn test_offset_str() {
+ let a = "abcřèÂßÇd123";
+ let b = &a[7..];
+ let c = &a[..5];
+ let d = &a[5..9];
+ assert_eq!(b.offset_from(&a), 7);
+ assert_eq!(c.offset_from(&a), 0);
+ assert_eq!(d.offset_from(&a), 5);
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn test_bit_stream_empty() {
+ let i = (&b""[..], 0);
+
+ let actual = i.iter_offsets().collect::<crate::lib::std::vec::Vec<_>>();
+ assert_eq!(actual, vec![]);
+
+ let actual = i.eof_offset();
+ assert_eq!(actual, 0);
+
+ let actual = i.peek_token();
+ assert_eq!(actual, None);
+
+ let actual = i.offset_for(|b| b);
+ assert_eq!(actual, None);
+
+ let actual = i.offset_at(1);
+ assert_eq!(actual, Err(Needed::new(1)));
+
+ let (actual_input, actual_slice) = i.peek_slice(0);
+ assert_eq!(actual_input, (&b""[..], 0));
+ assert_eq!(actual_slice, (&b""[..], 0, 0));
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn test_bit_offset_empty() {
+ let i = (&b""[..], 0);
+
+ let actual = i.offset_from(&i);
+ assert_eq!(actual, 0);
+}
+
+#[cfg(feature = "std")]
+proptest! {
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn bit_stream(byte_len in 0..20usize, start in 0..160usize) {
+ bit_stream_inner(byte_len, start);
+ }
+}
+
+#[cfg(feature = "std")]
+fn bit_stream_inner(byte_len: usize, start: usize) {
+ let start = start.min(byte_len * 8);
+ let start_byte = start / 8;
+ let start_bit = start % 8;
+
+ let bytes = vec![0b1010_1010; byte_len];
+ let i = (&bytes[start_byte..], start_bit);
+
+ let mut curr_i = i;
+ let mut curr_offset = 0;
+ while let Some((next_i, _token)) = curr_i.peek_token() {
+ let to_offset = curr_i.offset_from(&i);
+ assert_eq!(curr_offset, to_offset);
+
+ let (slice_i, _) = i.peek_slice(curr_offset);
+ assert_eq!(curr_i, slice_i);
+
+ let at_offset = i.offset_at(curr_offset).unwrap();
+ assert_eq!(curr_offset, at_offset);
+
+ let eof_offset = curr_i.eof_offset();
+ let (next_eof_i, eof_slice) = curr_i.peek_slice(eof_offset);
+ assert_eq!(next_eof_i, (&b""[..], 0));
+ let eof_slice_i = (eof_slice.0, eof_slice.1);
+ assert_eq!(eof_slice_i, curr_i);
+
+ curr_offset += 1;
+ curr_i = next_i;
+ }
+ assert_eq!(i.eof_offset(), curr_offset);
+}
+
+#[test]
+fn test_partial_complete() {
+ let mut i = Partial::new(&b""[..]);
+ assert!(Partial::<&[u8]>::is_partial_supported());
+
+ assert!(i.is_partial(), "incomplete by default");
+ let incomplete_state = i.complete();
+ assert!(!i.is_partial(), "the stream should be marked as complete");
+
+ i.restore_partial(incomplete_state);
+ assert!(i.is_partial(), "incomplete stream state should be restored");
+}
diff --git a/src/token/mod.rs b/src/token/mod.rs
new file mode 100644
index 0000000..fba019c
--- /dev/null
+++ b/src/token/mod.rs
@@ -0,0 +1,1062 @@
+//! Parsers extracting tokens from the stream
+
+#[cfg(test)]
+mod tests;
+
+use crate::error::ErrMode;
+use crate::error::ErrorKind;
+use crate::error::Needed;
+use crate::error::ParserError;
+use crate::lib::std::result::Result::Ok;
+use crate::stream::Range;
+use crate::stream::{Compare, CompareResult, ContainsToken, FindSlice, SliceLen, Stream};
+use crate::stream::{StreamIsPartial, ToUsize};
+use crate::trace::trace;
+use crate::PResult;
+use crate::Parser;
+
+/// Matches one token
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{token::any, error::ErrMode, error::{InputError, ErrorKind}};
+/// # use winnow::prelude::*;
+/// fn parser(input: &str) -> IResult<&str, char> {
+/// any.parse_peek(input)
+/// }
+///
+/// assert_eq!(parser("abc"), Ok(("bc",'a')));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{token::any, error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// assert_eq!(any::<_, InputError<_>>.parse_peek(Partial::new("abc")), Ok((Partial::new("bc"),'a')));
+/// assert_eq!(any::<_, InputError<_>>.parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+#[doc(alias = "token")]
+pub fn any<I, E: ParserError<I>>(input: &mut I) -> PResult<<I as Stream>::Token, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+{
+ trace("any", move |input: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ any_::<_, _, true>(input)
+ } else {
+ any_::<_, _, true>(input)
+ }
+ })
+ .parse_next(input)
+}
+
+fn any_<I, E: ParserError<I>, const PARTIAL: bool>(
+ input: &mut I,
+) -> PResult<<I as Stream>::Token, E>
+where
+ I: StreamIsPartial,
+ I: Stream,
+{
+ input.next_token().ok_or_else(|| {
+ if PARTIAL && input.is_partial() {
+ ErrMode::Incomplete(Needed::new(1))
+ } else {
+ ErrMode::from_error_kind(input, ErrorKind::Token)
+ }
+ })
+}
+
+/// Recognizes a literal
+///
+/// The input data will be compared to the tag combinator's argument and will return the part of
+/// the input that matches the argument
+///
+/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern
+///
+/// **Note:** [`Parser`][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<Partial<&str>, &str> {
+/// "Hello".parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new("Hello, World!")), Ok((Partial::new(", World!"), "Hello")));
+/// assert_eq!(parser(Partial::new("Something")), Err(ErrMode::Backtrack(InputError::new(Partial::new("Something"), ErrorKind::Tag))));
+/// assert_eq!(parser(Partial::new("S")), Err(ErrMode::Backtrack(InputError::new(Partial::new("S"), ErrorKind::Tag))));
+/// assert_eq!(parser(Partial::new("H")), Err(ErrMode::Incomplete(Needed::new(4))));
+/// ```
+#[inline(always)]
+#[doc(alias = "literal")]
+#[doc(alias = "bytes")]
+#[doc(alias = "just")]
+pub fn tag<T, I, Error: ParserError<I>>(tag: T) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + Compare<T>,
+ T: SliceLen + Clone,
+{
+ trace("tag", move |i: &mut I| {
+ let t = tag.clone();
+ if <I as StreamIsPartial>::is_partial_supported() {
+ tag_::<_, _, _, true>(i, t)
+ } else {
+ tag_::<_, _, _, false>(i, t)
+ }
+ })
+}
+
+fn tag_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ i: &mut I,
+ t: T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + Compare<T>,
+ T: SliceLen,
+{
+ let tag_len = t.slice_len();
+ match i.compare(t) {
+ CompareResult::Ok => Ok(i.next_slice(tag_len)),
+ CompareResult::Incomplete if PARTIAL && i.is_partial() => {
+ Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset())))
+ }
+ CompareResult::Incomplete | CompareResult::Error => {
+ let e: ErrorKind = ErrorKind::Tag;
+ Err(ErrMode::from_error_kind(i, e))
+ }
+ }
+}
+
+/// Recognizes a case insensitive literal.
+///
+/// The input data will be compared to the tag combinator's argument and will return the part of
+/// the input that matches the argument with no regard to case.
+///
+/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Tag)))` if the input doesn't match the pattern.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::tag_no_case;
+///
+/// fn parser(s: &str) -> IResult<&str, &str> {
+/// tag_no_case("hello").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser("Hello, World!"), Ok((", World!", "Hello")));
+/// assert_eq!(parser("hello, World!"), Ok((", World!", "hello")));
+/// assert_eq!(parser("HeLlO, World!"), Ok((", World!", "HeLlO")));
+/// assert_eq!(parser("Something"), Err(ErrMode::Backtrack(InputError::new("Something", ErrorKind::Tag))));
+/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Tag))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::tag_no_case;
+///
+/// fn parser(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// tag_no_case("hello").parse_peek(s)
+/// }
+///
+/// assert_eq!(parser(Partial::new("Hello, World!")), Ok((Partial::new(", World!"), "Hello")));
+/// assert_eq!(parser(Partial::new("hello, World!")), Ok((Partial::new(", World!"), "hello")));
+/// assert_eq!(parser(Partial::new("HeLlO, World!")), Ok((Partial::new(", World!"), "HeLlO")));
+/// assert_eq!(parser(Partial::new("Something")), Err(ErrMode::Backtrack(InputError::new(Partial::new("Something"), ErrorKind::Tag))));
+/// assert_eq!(parser(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(5))));
+/// ```
+#[inline(always)]
+#[doc(alias = "literal")]
+#[doc(alias = "bytes")]
+#[doc(alias = "just")]
+pub fn tag_no_case<T, I, Error: ParserError<I>>(
+ tag: T,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + Compare<T>,
+ T: SliceLen + Clone,
+{
+ trace("tag_no_case", move |i: &mut I| {
+ let t = tag.clone();
+ if <I as StreamIsPartial>::is_partial_supported() {
+ tag_no_case_::<_, _, _, true>(i, t)
+ } else {
+ tag_no_case_::<_, _, _, false>(i, t)
+ }
+ })
+}
+
+fn tag_no_case_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ i: &mut I,
+ t: T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + Compare<T>,
+ T: SliceLen,
+{
+ let tag_len = t.slice_len();
+
+ match i.compare_no_case(t) {
+ CompareResult::Ok => Ok(i.next_slice(tag_len)),
+ CompareResult::Incomplete if PARTIAL && i.is_partial() => {
+ Err(ErrMode::Incomplete(Needed::new(tag_len - i.eof_offset())))
+ }
+ CompareResult::Incomplete | CompareResult::Error => {
+ let e: ErrorKind = ErrorKind::Tag;
+ Err(ErrMode::from_error_kind(i, e))
+ }
+ }
+}
+
+/// Recognize a token that matches the [pattern][ContainsToken]
+///
+/// **Note:** [`Parser`][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<Partial<&str>, char> {
+/// one_of(|c| c == 'a' || c == 'b').parse_peek(i)
+/// }
+/// assert_eq!(parser_fn(Partial::new("abc")), Ok((Partial::new("bc"), 'a')));
+/// assert_eq!(parser_fn(Partial::new("cd")), Err(ErrMode::Backtrack(InputError::new(Partial::new("cd"), ErrorKind::Verify))));
+/// assert_eq!(parser_fn(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+#[doc(alias = "char")]
+#[doc(alias = "token")]
+#[doc(alias = "satisfy")]
+pub fn one_of<I, T, Error: ParserError<I>>(list: T) -> impl Parser<I, <I as Stream>::Token, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: Clone,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ trace(
+ "one_of",
+ any.verify(move |t: &<I as Stream>::Token| list.contains_token(t.clone())),
+ )
+}
+
+/// Recognize a token that does not match the [pattern][ContainsToken]
+///
+/// *Complete version*: Will return an error if there's not enough input data.
+///
+/// *Partial version*: Will return `Err(winnow::error::ErrMode::Incomplete(_))` if there's not enough input data.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError};
+/// # use winnow::prelude::*;
+/// # use winnow::token::none_of;
+/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek("z"), Ok(("", 'z')));
+/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b']).parse_peek("a"), Err(ErrMode::Backtrack(InputError::new("a", ErrorKind::Verify))));
+/// assert_eq!(none_of::<_, _, InputError<_>>('a').parse_peek(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Token))));
+/// ```
+///
+/// ```
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// # use winnow::token::none_of;
+/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b', 'c']).parse_peek(Partial::new("z")), Ok((Partial::new(""), 'z')));
+/// assert_eq!(none_of::<_, _, InputError<_>>(['a', 'b']).parse_peek(Partial::new("a")), Err(ErrMode::Backtrack(InputError::new(Partial::new("a"), ErrorKind::Verify))));
+/// assert_eq!(none_of::<_, _, InputError<_>>('a').parse_peek(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn none_of<I, T, Error: ParserError<I>>(list: T) -> impl Parser<I, <I as Stream>::Token, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ <I as Stream>::Token: Clone,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ trace(
+ "none_of",
+ any.verify(move |t: &<I as Stream>::Token| !list.contains_token(t.clone())),
+ )
+}
+
+/// Recognize the longest (m <= len <= n) input slice that matches the [pattern][ContainsToken]
+///
+/// It will return an `ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice))` if the pattern wasn't met or is out
+/// of range (m <= len <= n).
+///
+/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the pattern reaches the end of the input or is too short.
+///
+/// To recognize a series of tokens, use [`repeat`][crate::combinator::repeat] to [`Accumulate`][crate::stream::Accumulate] into a `()` and then [`Parser::recognize`][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<Partial<&[u8]>, &[u8]> {
+/// take_while(0.., AsChar::is_alpha).parse_peek(s)
+/// }
+///
+/// assert_eq!(alpha(Partial::new(b"latin123")), Ok((Partial::new(&b"123"[..]), &b"latin"[..])));
+/// assert_eq!(alpha(Partial::new(b"12345")), Ok((Partial::new(&b"12345"[..]), &b""[..])));
+/// assert_eq!(alpha(Partial::new(b"latin")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(alpha(Partial::new(b"")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+///
+/// One or more tokens:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take_while;
+/// use winnow::stream::AsChar;
+///
+/// fn alpha(s: &[u8]) -> IResult<&[u8], &[u8]> {
+/// take_while(1.., AsChar::is_alpha).parse_peek(s)
+/// }
+///
+/// assert_eq!(alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..])));
+/// assert_eq!(alpha(b"latin"), Ok((&b""[..], &b"latin"[..])));
+/// assert_eq!(alpha(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice))));
+///
+/// fn hex(s: &str) -> IResult<&str, &str> {
+/// take_while(1.., ('0'..='9', 'A'..='F')).parse_peek(s)
+/// }
+///
+/// assert_eq!(hex("123 and voila"), Ok((" and voila", "123")));
+/// assert_eq!(hex("DEADBEEF and others"), Ok((" and others", "DEADBEEF")));
+/// assert_eq!(hex("BADBABEsomething"), Ok(("something", "BADBABE")));
+/// assert_eq!(hex("D15EA5E"), Ok(("", "D15EA5E")));
+/// assert_eq!(hex(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::take_while;
+/// use winnow::stream::AsChar;
+///
+/// fn alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+/// take_while(1.., AsChar::is_alpha).parse_peek(s)
+/// }
+///
+/// assert_eq!(alpha(Partial::new(b"latin123")), Ok((Partial::new(&b"123"[..]), &b"latin"[..])));
+/// assert_eq!(alpha(Partial::new(b"latin")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(alpha(Partial::new(b"12345")), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"12345"[..]), ErrorKind::Slice))));
+///
+/// fn hex(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take_while(1.., ('0'..='9', 'A'..='F')).parse_peek(s)
+/// }
+///
+/// assert_eq!(hex(Partial::new("123 and voila")), Ok((Partial::new(" and voila"), "123")));
+/// assert_eq!(hex(Partial::new("DEADBEEF and others")), Ok((Partial::new(" and others"), "DEADBEEF")));
+/// assert_eq!(hex(Partial::new("BADBABEsomething")), Ok((Partial::new("something"), "BADBABE")));
+/// assert_eq!(hex(Partial::new("D15EA5E")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(hex(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+///
+/// Arbitrary amount of tokens:
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take_while;
+/// use winnow::stream::AsChar;
+///
+/// fn short_alpha(s: &[u8]) -> IResult<&[u8], &[u8]> {
+/// take_while(3..=6, AsChar::is_alpha).parse_peek(s)
+/// }
+///
+/// assert_eq!(short_alpha(b"latin123"), Ok((&b"123"[..], &b"latin"[..])));
+/// assert_eq!(short_alpha(b"lengthy"), Ok((&b"y"[..], &b"length"[..])));
+/// assert_eq!(short_alpha(b"latin"), Ok((&b""[..], &b"latin"[..])));
+/// assert_eq!(short_alpha(b"ed"), Err(ErrMode::Backtrack(InputError::new(&b"ed"[..], ErrorKind::Slice))));
+/// assert_eq!(short_alpha(b"12345"), Err(ErrMode::Backtrack(InputError::new(&b"12345"[..], ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::take_while;
+/// use winnow::stream::AsChar;
+///
+/// fn short_alpha(s: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+/// take_while(3..=6, AsChar::is_alpha).parse_peek(s)
+/// }
+///
+/// assert_eq!(short_alpha(Partial::new(b"latin123")), Ok((Partial::new(&b"123"[..]), &b"latin"[..])));
+/// assert_eq!(short_alpha(Partial::new(b"lengthy")), Ok((Partial::new(&b"y"[..]), &b"length"[..])));
+/// assert_eq!(short_alpha(Partial::new(b"latin")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(short_alpha(Partial::new(b"ed")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(short_alpha(Partial::new(b"12345")), Err(ErrMode::Backtrack(InputError::new(Partial::new(&b"12345"[..]), ErrorKind::Slice))));
+/// ```
+#[inline(always)]
+#[doc(alias = "is_a")]
+#[doc(alias = "take_while0")]
+#[doc(alias = "take_while1")]
+pub fn take_while<T, I, Error: ParserError<I>>(
+ range: impl Into<Range>,
+ list: T,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ let Range {
+ start_inclusive,
+ end_inclusive,
+ } = range.into();
+ trace("take_while", move |i: &mut I| {
+ match (start_inclusive, end_inclusive) {
+ (0, None) => {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_while0_::<_, _, _, true>(i, &list)
+ } else {
+ take_while0_::<_, _, _, false>(i, &list)
+ }
+ }
+ (1, None) => {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_while1_::<_, _, _, true>(i, &list)
+ } else {
+ take_while1_::<_, _, _, false>(i, &list)
+ }
+ }
+ (start, end) => {
+ let end = end.unwrap_or(usize::MAX);
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_while_m_n_::<_, _, _, true>(i, start, end, &list)
+ } else {
+ take_while_m_n_::<_, _, _, false>(i, start, end, &list)
+ }
+ }
+ }
+ })
+}
+
+fn take_while0_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ input: &mut I,
+ list: &T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ if PARTIAL && input.is_partial() {
+ take_till0_partial(input, |c| !list.contains_token(c))
+ } else {
+ take_till0_complete(input, |c| !list.contains_token(c))
+ }
+}
+
+fn take_while1_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ input: &mut I,
+ list: &T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ let e: ErrorKind = ErrorKind::Slice;
+ if PARTIAL && input.is_partial() {
+ take_till1_partial(input, |c| !list.contains_token(c), e)
+ } else {
+ take_till1_complete(input, |c| !list.contains_token(c), e)
+ }
+}
+
+/// Looks for the first element of the input type for which the condition returns true,
+/// and returns the input up to this position.
+///
+/// *Partial version*: If no element is found matching the condition, this will return `Incomplete`
+fn take_till0_partial<P, I: Stream, E: ParserError<I>>(
+ input: &mut I,
+ predicate: P,
+) -> PResult<<I as Stream>::Slice, E>
+where
+ P: Fn(I::Token) -> bool,
+{
+ let offset = input
+ .offset_for(predicate)
+ .ok_or_else(|| ErrMode::Incomplete(Needed::new(1)))?;
+ Ok(input.next_slice(offset))
+}
+
+/// Looks for the first element of the input type for which the condition returns true
+/// and returns the input up to this position.
+///
+/// Fails if the produced slice is empty.
+///
+/// *Partial version*: If no element is found matching the condition, this will return `Incomplete`
+fn take_till1_partial<P, I: Stream, E: ParserError<I>>(
+ input: &mut I,
+ predicate: P,
+ e: ErrorKind,
+) -> PResult<<I as Stream>::Slice, E>
+where
+ P: Fn(I::Token) -> bool,
+{
+ let offset = input
+ .offset_for(predicate)
+ .ok_or_else(|| ErrMode::Incomplete(Needed::new(1)))?;
+ if offset == 0 {
+ Err(ErrMode::from_error_kind(input, e))
+ } else {
+ Ok(input.next_slice(offset))
+ }
+}
+
+/// Looks for the first element of the input type for which the condition returns true,
+/// and returns the input up to this position.
+///
+/// *Complete version*: If no element is found matching the condition, this will return the whole input
+fn take_till0_complete<P, I: Stream, E: ParserError<I>>(
+ input: &mut I,
+ predicate: P,
+) -> PResult<<I as Stream>::Slice, E>
+where
+ P: Fn(I::Token) -> bool,
+{
+ let offset = input
+ .offset_for(predicate)
+ .unwrap_or_else(|| input.eof_offset());
+ Ok(input.next_slice(offset))
+}
+
+/// Looks for the first element of the input type for which the condition returns true
+/// and returns the input up to this position.
+///
+/// Fails if the produced slice is empty.
+///
+/// *Complete version*: If no element is found matching the condition, this will return the whole input
+fn take_till1_complete<P, I: Stream, E: ParserError<I>>(
+ input: &mut I,
+ predicate: P,
+ e: ErrorKind,
+) -> PResult<<I as Stream>::Slice, E>
+where
+ P: Fn(I::Token) -> bool,
+{
+ let offset = input
+ .offset_for(predicate)
+ .unwrap_or_else(|| input.eof_offset());
+ if offset == 0 {
+ Err(ErrMode::from_error_kind(input, e))
+ } else {
+ Ok(input.next_slice(offset))
+ }
+}
+
+fn take_while_m_n_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ input: &mut I,
+ m: usize,
+ n: usize,
+ list: &T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ if n < m {
+ return Err(ErrMode::assert(input, "`m` should be <= `n`"));
+ }
+
+ let mut final_count = 0;
+ for (processed, (offset, token)) in input.iter_offsets().enumerate() {
+ if !list.contains_token(token) {
+ if processed < m {
+ return Err(ErrMode::from_error_kind(input, ErrorKind::Slice));
+ } else {
+ return Ok(input.next_slice(offset));
+ }
+ } else {
+ if processed == n {
+ return Ok(input.next_slice(offset));
+ }
+ final_count = processed + 1;
+ }
+ }
+ if PARTIAL && input.is_partial() {
+ if final_count == n {
+ Ok(input.finish())
+ } else {
+ let needed = if m > input.eof_offset() {
+ m - input.eof_offset()
+ } else {
+ 1
+ };
+ Err(ErrMode::Incomplete(Needed::new(needed)))
+ }
+ } else {
+ if m <= final_count {
+ Ok(input.finish())
+ } else {
+ Err(ErrMode::from_error_kind(input, ErrorKind::Slice))
+ }
+ }
+}
+
+/// Recognize the longest input slice (if any) till a [pattern][ContainsToken] is met.
+///
+/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the
+/// end of input or if there was not match.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take_till0;
+///
+/// fn till_colon(s: &str) -> IResult<&str, &str> {
+/// take_till0(|c| c == ':').parse_peek(s)
+/// }
+///
+/// assert_eq!(till_colon("latin:123"), Ok((":123", "latin")));
+/// assert_eq!(till_colon(":empty matched"), Ok((":empty matched", ""))); //allowed
+/// assert_eq!(till_colon("12345"), Ok(("", "12345")));
+/// assert_eq!(till_colon(""), Ok(("", "")));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::take_till0;
+///
+/// fn till_colon(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take_till0(|c| c == ':').parse_peek(s)
+/// }
+///
+/// assert_eq!(till_colon(Partial::new("latin:123")), Ok((Partial::new(":123"), "latin")));
+/// assert_eq!(till_colon(Partial::new(":empty matched")), Ok((Partial::new(":empty matched"), ""))); //allowed
+/// assert_eq!(till_colon(Partial::new("12345")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(till_colon(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+pub fn take_till0<T, I, Error: ParserError<I>>(
+ list: T,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ trace("take_till0", move |i: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() {
+ take_till0_partial(i, |c| list.contains_token(c))
+ } else {
+ take_till0_complete(i, |c| list.contains_token(c))
+ }
+ })
+}
+
+/// Recognize the longest (at least 1) input slice till a [pattern][ContainsToken] is met.
+///
+/// It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is empty or the
+/// predicate matches the first input.
+///
+/// *Partial version* will return a `ErrMode::Incomplete(Needed::new(1))` if the match reaches the
+/// end of input or if there was not match.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take_till1;
+///
+/// fn till_colon(s: &str) -> IResult<&str, &str> {
+/// take_till1(|c| c == ':').parse_peek(s)
+/// }
+///
+/// assert_eq!(till_colon("latin:123"), Ok((":123", "latin")));
+/// assert_eq!(till_colon(":empty matched"), Err(ErrMode::Backtrack(InputError::new(":empty matched", ErrorKind::Slice))));
+/// assert_eq!(till_colon("12345"), Ok(("", "12345")));
+/// assert_eq!(till_colon(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+///
+/// fn not_space(s: &str) -> IResult<&str, &str> {
+/// take_till1([' ', '\t', '\r', '\n']).parse_peek(s)
+/// }
+///
+/// assert_eq!(not_space("Hello, World!"), Ok((" World!", "Hello,")));
+/// assert_eq!(not_space("Sometimes\t"), Ok(("\t", "Sometimes")));
+/// assert_eq!(not_space("Nospace"), Ok(("", "Nospace")));
+/// assert_eq!(not_space(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::take_till1;
+///
+/// fn till_colon(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take_till1(|c| c == ':').parse_peek(s)
+/// }
+///
+/// assert_eq!(till_colon(Partial::new("latin:123")), Ok((Partial::new(":123"), "latin")));
+/// assert_eq!(till_colon(Partial::new(":empty matched")), Err(ErrMode::Backtrack(InputError::new(Partial::new(":empty matched"), ErrorKind::Slice))));
+/// assert_eq!(till_colon(Partial::new("12345")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(till_colon(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+///
+/// fn not_space(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take_till1([' ', '\t', '\r', '\n']).parse_peek(s)
+/// }
+///
+/// assert_eq!(not_space(Partial::new("Hello, World!")), Ok((Partial::new(" World!"), "Hello,")));
+/// assert_eq!(not_space(Partial::new("Sometimes\t")), Ok((Partial::new("\t"), "Sometimes")));
+/// assert_eq!(not_space(Partial::new("Nospace")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// assert_eq!(not_space(Partial::new("")), Err(ErrMode::Incomplete(Needed::new(1))));
+/// ```
+#[inline(always)]
+#[doc(alias = "is_not")]
+pub fn take_till1<T, I, Error: ParserError<I>>(
+ list: T,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ T: ContainsToken<<I as Stream>::Token>,
+{
+ trace("take_till1", move |i: &mut I| {
+ let e: ErrorKind = ErrorKind::Slice;
+ if <I as StreamIsPartial>::is_partial_supported() && i.is_partial() {
+ take_till1_partial(i, |c| list.contains_token(c), e)
+ } else {
+ take_till1_complete(i, |c| list.contains_token(c), e)
+ }
+ })
+}
+
+/// Recognize an input slice containing the first N input elements (I[..N]).
+///
+/// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))` if the input is shorter than the argument.
+///
+/// *Partial version*: if the input has less than N elements, `take` will
+/// return a `ErrMode::Incomplete(Needed::new(M))` where M is the number of
+/// additional bytes the parser would need to succeed.
+/// It is well defined for `&[u8]` as the number of elements is the byte size,
+/// but for types like `&str`, we cannot know how many bytes correspond for
+/// the next few chars, so the result will be `ErrMode::Incomplete(Needed::Unknown)`
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take;
+///
+/// fn take6(s: &str) -> IResult<&str, &str> {
+/// take(6usize).parse_peek(s)
+/// }
+///
+/// assert_eq!(take6("1234567"), Ok(("7", "123456")));
+/// assert_eq!(take6("things"), Ok(("", "things")));
+/// assert_eq!(take6("short"), Err(ErrMode::Backtrack(InputError::new("short", ErrorKind::Slice))));
+/// assert_eq!(take6(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// ```
+///
+/// The units that are taken will depend on the input type. For example, for a
+/// `&str` it will take a number of `char`'s, whereas for a `&[u8]` it will
+/// take that many `u8`'s:
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// use winnow::error::InputError;
+/// use winnow::token::take;
+///
+/// assert_eq!(take::<_, _, InputError<_>>(1usize).parse_peek("💙"), Ok(("", "💙")));
+/// assert_eq!(take::<_, _, InputError<_>>(1usize).parse_peek("💙".as_bytes()), Ok((b"\x9F\x92\x99".as_ref(), b"\xF0".as_ref())));
+/// ```
+///
+/// ```rust
+/// # use winnow::prelude::*;
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::Partial;
+/// use winnow::token::take;
+///
+/// fn take6(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take(6usize).parse_peek(s)
+/// }
+///
+/// assert_eq!(take6(Partial::new("1234567")), Ok((Partial::new("7"), "123456")));
+/// assert_eq!(take6(Partial::new("things")), Ok((Partial::new(""), "things")));
+/// // `Unknown` as we don't know the number of bytes that `count` corresponds to
+/// assert_eq!(take6(Partial::new("short")), Err(ErrMode::Incomplete(Needed::Unknown)));
+/// ```
+#[inline(always)]
+pub fn take<C, I, Error: ParserError<I>>(count: C) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+ C: ToUsize,
+{
+ let c = count.to_usize();
+ trace("take", move |i: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_::<_, _, true>(i, c)
+ } else {
+ take_::<_, _, false>(i, c)
+ }
+ })
+}
+
+fn take_<I, Error: ParserError<I>, const PARTIAL: bool>(
+ i: &mut I,
+ c: usize,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream,
+{
+ match i.offset_at(c) {
+ Ok(offset) => Ok(i.next_slice(offset)),
+ Err(e) if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(e)),
+ Err(_needed) => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
+ }
+}
+
+/// Recognize the input slice up to the first occurrence of the literal.
+///
+/// It doesn't consume the pattern.
+///
+/// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))`
+/// if the pattern wasn't met.
+///
+/// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't
+/// contain the pattern or if the input is smaller than the pattern.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take_until0;
+///
+/// fn until_eof(s: &str) -> IResult<&str, &str> {
+/// take_until0("eof").parse_peek(s)
+/// }
+///
+/// assert_eq!(until_eof("hello, worldeof"), Ok(("eof", "hello, world")));
+/// assert_eq!(until_eof("hello, world"), Err(ErrMode::Backtrack(InputError::new("hello, world", ErrorKind::Slice))));
+/// assert_eq!(until_eof(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// assert_eq!(until_eof("1eof2eof"), Ok(("eof2eof", "1")));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::ErrorKind, error::InputError, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::take_until0;
+///
+/// fn until_eof(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take_until0("eof").parse_peek(s)
+/// }
+///
+/// assert_eq!(until_eof(Partial::new("hello, worldeof")), Ok((Partial::new("eof"), "hello, world")));
+/// assert_eq!(until_eof(Partial::new("hello, world")), Err(ErrMode::Incomplete(Needed::Unknown)));
+/// assert_eq!(until_eof(Partial::new("hello, worldeo")), Err(ErrMode::Incomplete(Needed::Unknown)));
+/// assert_eq!(until_eof(Partial::new("1eof2eof")), Ok((Partial::new("eof2eof"), "1")));
+/// ```
+#[inline(always)]
+pub fn take_until0<T, I, Error: ParserError<I>>(
+ tag: T,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + FindSlice<T>,
+ T: SliceLen + Clone,
+{
+ trace("take_until0", move |i: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_until0_::<_, _, _, true>(i, tag.clone())
+ } else {
+ take_until0_::<_, _, _, false>(i, tag.clone())
+ }
+ })
+}
+
+fn take_until0_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ i: &mut I,
+ t: T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + FindSlice<T>,
+ T: SliceLen,
+{
+ match i.find_slice(t) {
+ Some(offset) => Ok(i.next_slice(offset)),
+ None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)),
+ None => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
+ }
+}
+
+/// Recognize the non empty input slice up to the first occurrence of the literal.
+///
+/// It doesn't consume the pattern.
+///
+/// *Complete version*: It will return `Err(ErrMode::Backtrack(InputError::new(_, ErrorKind::Slice)))`
+/// if the pattern wasn't met.
+///
+/// *Partial version*: will return a `ErrMode::Incomplete(Needed::new(N))` if the input doesn't
+/// contain the pattern or if the input is smaller than the pattern.
+///
+/// # Example
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// use winnow::token::take_until1;
+///
+/// fn until_eof(s: &str) -> IResult<&str, &str> {
+/// take_until1("eof").parse_peek(s)
+/// }
+///
+/// assert_eq!(until_eof("hello, worldeof"), Ok(("eof", "hello, world")));
+/// assert_eq!(until_eof("hello, world"), Err(ErrMode::Backtrack(InputError::new("hello, world", ErrorKind::Slice))));
+/// assert_eq!(until_eof(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Slice))));
+/// assert_eq!(until_eof("1eof2eof"), Ok(("eof2eof", "1")));
+/// assert_eq!(until_eof("eof"), Err(ErrMode::Backtrack(InputError::new("eof", ErrorKind::Slice))));
+/// ```
+///
+/// ```rust
+/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed};
+/// # use winnow::prelude::*;
+/// # use winnow::Partial;
+/// use winnow::token::take_until1;
+///
+/// fn until_eof(s: Partial<&str>) -> IResult<Partial<&str>, &str> {
+/// take_until1("eof").parse_peek(s)
+/// }
+///
+/// assert_eq!(until_eof(Partial::new("hello, worldeof")), Ok((Partial::new("eof"), "hello, world")));
+/// assert_eq!(until_eof(Partial::new("hello, world")), Err(ErrMode::Incomplete(Needed::Unknown)));
+/// assert_eq!(until_eof(Partial::new("hello, worldeo")), Err(ErrMode::Incomplete(Needed::Unknown)));
+/// assert_eq!(until_eof(Partial::new("1eof2eof")), Ok((Partial::new("eof2eof"), "1")));
+/// assert_eq!(until_eof(Partial::new("eof")), Err(ErrMode::Backtrack(InputError::new(Partial::new("eof"), ErrorKind::Slice))));
+/// ```
+#[inline(always)]
+pub fn take_until1<T, I, Error: ParserError<I>>(
+ tag: T,
+) -> impl Parser<I, <I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + FindSlice<T>,
+ T: SliceLen + Clone,
+{
+ trace("take_until1", move |i: &mut I| {
+ if <I as StreamIsPartial>::is_partial_supported() {
+ take_until1_::<_, _, _, true>(i, tag.clone())
+ } else {
+ take_until1_::<_, _, _, false>(i, tag.clone())
+ }
+ })
+}
+
+fn take_until1_<T, I, Error: ParserError<I>, const PARTIAL: bool>(
+ i: &mut I,
+ t: T,
+) -> PResult<<I as Stream>::Slice, Error>
+where
+ I: StreamIsPartial,
+ I: Stream + FindSlice<T>,
+ T: SliceLen,
+{
+ match i.find_slice(t) {
+ None if PARTIAL && i.is_partial() => Err(ErrMode::Incomplete(Needed::Unknown)),
+ None | Some(0) => Err(ErrMode::from_error_kind(i, ErrorKind::Slice)),
+ Some(offset) => Ok(i.next_slice(offset)),
+ }
+}
diff --git a/src/token/tests.rs b/src/token/tests.rs
new file mode 100644
index 0000000..d9f3646
--- /dev/null
+++ b/src/token/tests.rs
@@ -0,0 +1,701 @@
+use super::*;
+
+#[cfg(feature = "std")]
+use proptest::prelude::*;
+
+use crate::binary::length_data;
+use crate::combinator::delimited;
+use crate::error::ErrMode;
+use crate::error::ErrorKind;
+use crate::error::InputError;
+use crate::error::Needed;
+use crate::stream::AsChar;
+use crate::token::tag;
+use crate::unpeek;
+use crate::IResult;
+use crate::Parser;
+use crate::Partial;
+
+#[test]
+fn complete_take_while_m_n_utf8_all_matching() {
+ let result: IResult<&str, &str> =
+ take_while(1..=4, |c: char| c.is_alphabetic()).parse_peek("øn");
+ assert_eq!(result, Ok(("", "øn")));
+}
+
+#[test]
+fn complete_take_while_m_n_utf8_all_matching_substring() {
+ let result: IResult<&str, &str> = take_while(1, |c: char| c.is_alphabetic()).parse_peek("øn");
+ assert_eq!(result, Ok(("n", "ø")));
+}
+
+#[cfg(feature = "std")]
+fn model_complete_take_while_m_n(
+ m: usize,
+ n: usize,
+ valid: usize,
+ input: &str,
+) -> IResult<&str, &str> {
+ if n < m {
+ Err(crate::error::ErrMode::from_error_kind(
+ &input,
+ crate::error::ErrorKind::Slice,
+ ))
+ } else if m <= valid {
+ let offset = n.min(valid);
+ Ok((&input[offset..], &input[0..offset]))
+ } else {
+ Err(crate::error::ErrMode::from_error_kind(
+ &input,
+ crate::error::ErrorKind::Slice,
+ ))
+ }
+}
+
+#[cfg(feature = "std")]
+proptest! {
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn complete_take_while_m_n_bounds(m in 0..20usize, n in 0..20usize, valid in 0..20usize, invalid in 0..20usize) {
+ let input = format!("{:a<valid$}{:b<invalid$}", "", "", valid=valid, invalid=invalid);
+ let expected = model_complete_take_while_m_n(m, n, valid, &input);
+ if m <= n {
+ let actual = take_while(m..=n, |c: char| c == 'a').parse_peek(input.as_str());
+ assert_eq!(expected, actual);
+ }
+ }
+}
+
+#[test]
+fn partial_any_str() {
+ use super::any;
+ assert_eq!(
+ any::<_, InputError<Partial<&str>>>.parse_peek(Partial::new("Ә")),
+ Ok((Partial::new(""), 'Ә'))
+ );
+}
+
+#[test]
+fn partial_one_of_test() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+ one_of(['a', 'b']).parse_peek(i)
+ }
+
+ let a = &b"abcd"[..];
+ assert_eq!(f(Partial::new(a)), Ok((Partial::new(&b"bcd"[..]), b'a')));
+
+ let b = &b"cde"[..];
+ assert_eq!(
+ f(Partial::new(b)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(b),
+ ErrorKind::Verify
+ )))
+ );
+
+ fn utf8(i: Partial<&str>) -> IResult<Partial<&str>, char> {
+ one_of(['+', '\u{FF0B}']).parse_peek(i)
+ }
+
+ assert!(utf8(Partial::new("+")).is_ok());
+ assert!(utf8(Partial::new("\u{FF0B}")).is_ok());
+}
+
+#[test]
+fn char_byteslice() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+ 'c'.parse_peek(i)
+ }
+
+ let a = &b"abcd"[..];
+ assert_eq!(
+ f(Partial::new(a)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(a),
+ ErrorKind::Verify
+ )))
+ );
+
+ let b = &b"cde"[..];
+ assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), b'c')));
+}
+
+#[test]
+fn char_str() {
+ fn f(i: Partial<&str>) -> IResult<Partial<&str>, char> {
+ 'c'.parse_peek(i)
+ }
+
+ let a = "abcd";
+ assert_eq!(
+ f(Partial::new(a)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(a),
+ ErrorKind::Verify
+ )))
+ );
+
+ let b = "cde";
+ assert_eq!(f(Partial::new(b)), Ok((Partial::new("de"), 'c')));
+}
+
+#[test]
+fn partial_none_of_test() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, u8> {
+ none_of(['a', 'b']).parse_peek(i)
+ }
+
+ let a = &b"abcd"[..];
+ assert_eq!(
+ f(Partial::new(a)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(a),
+ ErrorKind::Verify
+ )))
+ );
+
+ let b = &b"cde"[..];
+ assert_eq!(f(Partial::new(b)), Ok((Partial::new(&b"de"[..]), b'c')));
+}
+
+#[test]
+fn partial_is_a() {
+ fn a_or_b(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_while(1.., ['a', 'b']).parse_peek(i)
+ }
+
+ let a = Partial::new(&b"abcd"[..]);
+ assert_eq!(a_or_b(a), Ok((Partial::new(&b"cd"[..]), &b"ab"[..])));
+
+ let b = Partial::new(&b"bcde"[..]);
+ assert_eq!(a_or_b(b), Ok((Partial::new(&b"cde"[..]), &b"b"[..])));
+
+ let c = Partial::new(&b"cdef"[..]);
+ assert_eq!(
+ a_or_b(c),
+ Err(ErrMode::Backtrack(error_position!(&c, ErrorKind::Slice)))
+ );
+
+ let d = Partial::new(&b"bacdef"[..]);
+ assert_eq!(a_or_b(d), Ok((Partial::new(&b"cdef"[..]), &b"ba"[..])));
+}
+
+#[test]
+fn partial_is_not() {
+ fn a_or_b(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_till1(['a', 'b']).parse_peek(i)
+ }
+
+ let a = Partial::new(&b"cdab"[..]);
+ assert_eq!(a_or_b(a), Ok((Partial::new(&b"ab"[..]), &b"cd"[..])));
+
+ let b = Partial::new(&b"cbde"[..]);
+ assert_eq!(a_or_b(b), Ok((Partial::new(&b"bde"[..]), &b"c"[..])));
+
+ let c = Partial::new(&b"abab"[..]);
+ assert_eq!(
+ a_or_b(c),
+ Err(ErrMode::Backtrack(error_position!(&c, ErrorKind::Slice)))
+ );
+
+ let d = Partial::new(&b"cdefba"[..]);
+ assert_eq!(a_or_b(d), Ok((Partial::new(&b"ba"[..]), &b"cdef"[..])));
+
+ let e = Partial::new(&b"e"[..]);
+ assert_eq!(a_or_b(e), Err(ErrMode::Incomplete(Needed::new(1))));
+}
+
+#[test]
+fn partial_take_until_incomplete() {
+ fn y(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_until0("end").parse_peek(i)
+ }
+ assert_eq!(
+ y(Partial::new(&b"nd"[..])),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+ assert_eq!(
+ y(Partial::new(&b"123"[..])),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+ assert_eq!(
+ y(Partial::new(&b"123en"[..])),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+}
+
+#[test]
+fn partial_take_until_incomplete_s() {
+ fn ys(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_until0("end").parse_peek(i)
+ }
+ assert_eq!(
+ ys(Partial::new("123en")),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+}
+
+#[test]
+fn partial_recognize() {
+ use crate::ascii::{
+ alpha1 as alpha, alphanumeric1 as alphanumeric, digit1 as digit, hex_digit1 as hex_digit,
+ multispace1 as multispace, oct_digit1 as oct_digit, space1 as space,
+ };
+
+ fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ delimited("<!--", take(5_usize), "-->")
+ .recognize()
+ .parse_peek(i)
+ }
+ let r = x(Partial::new(&b"<!-- abc --> aaa"[..]));
+ assert_eq!(r, Ok((Partial::new(&b" aaa"[..]), &b"<!-- abc -->"[..])));
+
+ let semicolon = &b";"[..];
+
+ fn ya(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ alpha.recognize().parse_peek(i)
+ }
+ let ra = ya(Partial::new(&b"abc;"[..]));
+ assert_eq!(ra, Ok((Partial::new(semicolon), &b"abc"[..])));
+
+ fn yd(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ digit.recognize().parse_peek(i)
+ }
+ let rd = yd(Partial::new(&b"123;"[..]));
+ assert_eq!(rd, Ok((Partial::new(semicolon), &b"123"[..])));
+
+ fn yhd(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ hex_digit.recognize().parse_peek(i)
+ }
+ let rhd = yhd(Partial::new(&b"123abcDEF;"[..]));
+ assert_eq!(rhd, Ok((Partial::new(semicolon), &b"123abcDEF"[..])));
+
+ fn yod(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ oct_digit.recognize().parse_peek(i)
+ }
+ let rod = yod(Partial::new(&b"1234567;"[..]));
+ assert_eq!(rod, Ok((Partial::new(semicolon), &b"1234567"[..])));
+
+ fn yan(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ alphanumeric.recognize().parse_peek(i)
+ }
+ let ran = yan(Partial::new(&b"123abc;"[..]));
+ assert_eq!(ran, Ok((Partial::new(semicolon), &b"123abc"[..])));
+
+ fn ys(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ space.recognize().parse_peek(i)
+ }
+ let rs = ys(Partial::new(&b" \t;"[..]));
+ assert_eq!(rs, Ok((Partial::new(semicolon), &b" \t"[..])));
+
+ fn yms(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ multispace.recognize().parse_peek(i)
+ }
+ let rms = yms(Partial::new(&b" \t\r\n;"[..]));
+ assert_eq!(rms, Ok((Partial::new(semicolon), &b" \t\r\n"[..])));
+}
+
+#[test]
+fn partial_take_while0() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_while(0.., AsChar::is_alpha).parse_peek(i)
+ }
+ let a = &b""[..];
+ let b = &b"abcd"[..];
+ let c = &b"abcd123"[..];
+ let d = &b"123"[..];
+
+ assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(f(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(f(Partial::new(c)), Ok((Partial::new(d), b)));
+ assert_eq!(f(Partial::new(d)), Ok((Partial::new(d), a)));
+}
+
+#[test]
+fn partial_take_while1() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_while(1.., AsChar::is_alpha).parse_peek(i)
+ }
+ let a = &b""[..];
+ let b = &b"abcd"[..];
+ let c = &b"abcd123"[..];
+ let d = &b"123"[..];
+
+ assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(f(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(f(Partial::new(c)), Ok((Partial::new(&b"123"[..]), b)));
+ assert_eq!(
+ f(Partial::new(d)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(d),
+ ErrorKind::Slice
+ )))
+ );
+}
+
+#[test]
+fn partial_take_while_m_n() {
+ fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_while(2..=4, AsChar::is_alpha).parse_peek(i)
+ }
+ let a = &b""[..];
+ let b = &b"a"[..];
+ let c = &b"abc"[..];
+ let d = &b"abc123"[..];
+ let e = &b"abcde"[..];
+ let f = &b"123"[..];
+
+ assert_eq!(x(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(2))));
+ assert_eq!(x(Partial::new(b)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(x(Partial::new(c)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(x(Partial::new(d)), Ok((Partial::new(&b"123"[..]), c)));
+ assert_eq!(
+ x(Partial::new(e)),
+ Ok((Partial::new(&b"e"[..]), &b"abcd"[..]))
+ );
+ assert_eq!(
+ x(Partial::new(f)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(f),
+ ErrorKind::Slice
+ )))
+ );
+}
+
+#[test]
+fn partial_take_till0() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_till0(AsChar::is_alpha).parse_peek(i)
+ }
+ let a = &b""[..];
+ let b = &b"abcd"[..];
+ let c = &b"123abcd"[..];
+ let d = &b"123"[..];
+
+ assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(
+ f(Partial::new(b)),
+ Ok((Partial::new(&b"abcd"[..]), &b""[..]))
+ );
+ assert_eq!(
+ f(Partial::new(c)),
+ Ok((Partial::new(&b"abcd"[..]), &b"123"[..]))
+ );
+ assert_eq!(f(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1))));
+}
+
+#[test]
+fn partial_take_till1() {
+ fn f(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_till1(AsChar::is_alpha).parse_peek(i)
+ }
+ let a = &b""[..];
+ let b = &b"abcd"[..];
+ let c = &b"123abcd"[..];
+ let d = &b"123"[..];
+
+ assert_eq!(f(Partial::new(a)), Err(ErrMode::Incomplete(Needed::new(1))));
+ assert_eq!(
+ f(Partial::new(b)),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(b),
+ ErrorKind::Slice
+ )))
+ );
+ assert_eq!(
+ f(Partial::new(c)),
+ Ok((Partial::new(&b"abcd"[..]), &b"123"[..]))
+ );
+ assert_eq!(f(Partial::new(d)), Err(ErrMode::Incomplete(Needed::new(1))));
+}
+
+#[test]
+fn partial_take_while_utf8() {
+ fn f(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(0.., |c| c != '點').parse_peek(i)
+ }
+
+ assert_eq!(
+ f(Partial::new("")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ f(Partial::new("abcd")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(f(Partial::new("abcd點")), Ok((Partial::new("點"), "abcd")));
+ assert_eq!(
+ f(Partial::new("abcd點a")),
+ Ok((Partial::new("點a"), "abcd"))
+ );
+
+ fn g(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(0.., |c| c == '點').parse_peek(i)
+ }
+
+ assert_eq!(
+ g(Partial::new("")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點")));
+ assert_eq!(
+ g(Partial::new("點點點a")),
+ Ok((Partial::new("a"), "點點點"))
+ );
+}
+
+#[test]
+fn partial_take_till0_utf8() {
+ fn f(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_till0(|c| c == '點').parse_peek(i)
+ }
+
+ assert_eq!(
+ f(Partial::new("")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ f(Partial::new("abcd")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(f(Partial::new("abcd點")), Ok((Partial::new("點"), "abcd")));
+ assert_eq!(
+ f(Partial::new("abcd點a")),
+ Ok((Partial::new("點a"), "abcd"))
+ );
+
+ fn g(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_till0(|c| c != '點').parse_peek(i)
+ }
+
+ assert_eq!(
+ g(Partial::new("")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點")));
+ assert_eq!(
+ g(Partial::new("點點點a")),
+ Ok((Partial::new("a"), "點點點"))
+ );
+}
+
+#[test]
+fn partial_take_utf8() {
+ fn f(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take(3_usize).parse_peek(i)
+ }
+
+ assert_eq!(
+ f(Partial::new("")),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+ assert_eq!(
+ f(Partial::new("ab")),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+ assert_eq!(
+ f(Partial::new("點")),
+ Err(ErrMode::Incomplete(Needed::Unknown))
+ );
+ assert_eq!(f(Partial::new("ab點cd")), Ok((Partial::new("cd"), "ab點")));
+ assert_eq!(f(Partial::new("a點bcd")), Ok((Partial::new("cd"), "a點b")));
+ assert_eq!(f(Partial::new("a點b")), Ok((Partial::new(""), "a點b")));
+
+ fn g(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(0.., |c| c == '點').parse_peek(i)
+ }
+
+ assert_eq!(
+ g(Partial::new("")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(g(Partial::new("點abcd")), Ok((Partial::new("abcd"), "點")));
+ assert_eq!(
+ g(Partial::new("點點點a")),
+ Ok((Partial::new("a"), "點點點"))
+ );
+}
+
+#[test]
+fn partial_take_while_m_n_utf8_fixed() {
+ fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(1, |c| c == 'A' || c == '😃').parse_peek(i)
+ }
+ assert_eq!(parser(Partial::new("A!")), Ok((Partial::new("!"), "A")));
+ assert_eq!(parser(Partial::new("😃!")), Ok((Partial::new("!"), "😃")));
+}
+
+#[test]
+fn partial_take_while_m_n_utf8_range() {
+ fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(1..=2, |c| c == 'A' || c == '😃').parse_peek(i)
+ }
+ assert_eq!(parser(Partial::new("A!")), Ok((Partial::new("!"), "A")));
+ assert_eq!(parser(Partial::new("😃!")), Ok((Partial::new("!"), "😃")));
+}
+
+#[test]
+fn partial_take_while_m_n_utf8_full_match_fixed() {
+ fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(1, |c: char| c.is_alphabetic()).parse_peek(i)
+ }
+ assert_eq!(parser(Partial::new("øn")), Ok((Partial::new("n"), "ø")));
+}
+
+#[test]
+fn partial_take_while_m_n_utf8_full_match_range() {
+ fn parser(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ take_while(1..=2, |c: char| c.is_alphabetic()).parse_peek(i)
+ }
+ assert_eq!(parser(Partial::new("øn")), Ok((Partial::new(""), "øn")));
+}
+
+#[test]
+#[cfg(feature = "std")]
+fn partial_recognize_take_while0() {
+ fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ take_while(0.., AsChar::is_alphanum).parse_peek(i)
+ }
+ fn y(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ unpeek(x).recognize().parse_peek(i)
+ }
+ assert_eq!(
+ x(Partial::new(&b"ab."[..])),
+ Ok((Partial::new(&b"."[..]), &b"ab"[..]))
+ );
+ assert_eq!(
+ y(Partial::new(&b"ab."[..])),
+ Ok((Partial::new(&b"."[..]), &b"ab"[..]))
+ );
+}
+
+#[test]
+fn partial_length_bytes() {
+ use crate::binary::le_u8;
+
+ fn x(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ length_data(le_u8).parse_peek(i)
+ }
+ assert_eq!(
+ x(Partial::new(b"\x02..>>")),
+ Ok((Partial::new(&b">>"[..]), &b".."[..]))
+ );
+ assert_eq!(
+ x(Partial::new(b"\x02..")),
+ Ok((Partial::new(&[][..]), &b".."[..]))
+ );
+ assert_eq!(
+ x(Partial::new(b"\x02.")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ x(Partial::new(b"\x02")),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+
+ fn y(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ let (i, _) = "magic".parse_peek(i)?;
+ length_data(le_u8).parse_peek(i)
+ }
+ assert_eq!(
+ y(Partial::new(b"magic\x02..>>")),
+ Ok((Partial::new(&b">>"[..]), &b".."[..]))
+ );
+ assert_eq!(
+ y(Partial::new(b"magic\x02..")),
+ Ok((Partial::new(&[][..]), &b".."[..]))
+ );
+ assert_eq!(
+ y(Partial::new(b"magic\x02.")),
+ Err(ErrMode::Incomplete(Needed::new(1)))
+ );
+ assert_eq!(
+ y(Partial::new(b"magic\x02")),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+}
+
+#[cfg(feature = "alloc")]
+#[test]
+fn partial_case_insensitive() {
+ fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ tag_no_case("ABcd").parse_peek(i)
+ }
+ assert_eq!(
+ test(Partial::new(&b"aBCdefgh"[..])),
+ Ok((Partial::new(&b"efgh"[..]), &b"aBCd"[..]))
+ );
+ assert_eq!(
+ test(Partial::new(&b"abcdefgh"[..])),
+ Ok((Partial::new(&b"efgh"[..]), &b"abcd"[..]))
+ );
+ assert_eq!(
+ test(Partial::new(&b"ABCDefgh"[..])),
+ Ok((Partial::new(&b"efgh"[..]), &b"ABCD"[..]))
+ );
+ assert_eq!(
+ test(Partial::new(&b"ab"[..])),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ test(Partial::new(&b"Hello"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"Hello"[..]),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ test(Partial::new(&b"Hel"[..])),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new(&b"Hel"[..]),
+ ErrorKind::Tag
+ )))
+ );
+
+ fn test2(i: Partial<&str>) -> IResult<Partial<&str>, &str> {
+ tag_no_case("ABcd").parse_peek(i)
+ }
+ assert_eq!(
+ test2(Partial::new("aBCdefgh")),
+ Ok((Partial::new("efgh"), "aBCd"))
+ );
+ assert_eq!(
+ test2(Partial::new("abcdefgh")),
+ Ok((Partial::new("efgh"), "abcd"))
+ );
+ assert_eq!(
+ test2(Partial::new("ABCDefgh")),
+ Ok((Partial::new("efgh"), "ABCD"))
+ );
+ assert_eq!(
+ test2(Partial::new("ab")),
+ Err(ErrMode::Incomplete(Needed::new(2)))
+ );
+ assert_eq!(
+ test2(Partial::new("Hello")),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new("Hello"),
+ ErrorKind::Tag
+ )))
+ );
+ assert_eq!(
+ test2(Partial::new("Hel")),
+ Err(ErrMode::Backtrack(error_position!(
+ &Partial::new("Hel"),
+ ErrorKind::Tag
+ )))
+ );
+}
+
+#[test]
+fn partial_tag_fixed_size_array() {
+ fn test(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ tag([0x42]).parse_peek(i)
+ }
+ fn test2(i: Partial<&[u8]>) -> IResult<Partial<&[u8]>, &[u8]> {
+ tag(&[0x42]).parse_peek(i)
+ }
+ let input = Partial::new(&[0x42, 0x00][..]);
+ assert_eq!(test(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..])));
+ assert_eq!(test2(input), Ok((Partial::new(&b"\x00"[..]), &b"\x42"[..])));
+}
diff --git a/src/trace/internals.rs b/src/trace/internals.rs
new file mode 100644
index 0000000..136d21b
--- /dev/null
+++ b/src/trace/internals.rs
@@ -0,0 +1,244 @@
+#![cfg(feature = "std")]
+
+use std::io::Write;
+
+use crate::error::ErrMode;
+use crate::stream::Stream;
+
+pub struct Depth {
+ depth: usize,
+ inc: bool,
+}
+
+impl Depth {
+ pub fn new() -> Self {
+ let depth = DEPTH.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
+ let inc = true;
+ Self { depth, inc }
+ }
+
+ pub fn existing() -> Self {
+ let depth = DEPTH.load(std::sync::atomic::Ordering::SeqCst);
+ let inc = false;
+ Self { depth, inc }
+ }
+}
+
+impl Drop for Depth {
+ fn drop(&mut self) {
+ if self.inc {
+ let _ = DEPTH.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
+ }
+ }
+}
+
+impl AsRef<usize> for Depth {
+ #[inline(always)]
+ fn as_ref(&self) -> &usize {
+ &self.depth
+ }
+}
+
+impl crate::lib::std::ops::Deref for Depth {
+ type Target = usize;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.depth
+ }
+}
+
+static DEPTH: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
+
+pub enum Severity {
+ Success,
+ Backtrack,
+ Cut,
+ Incomplete,
+}
+
+impl Severity {
+ pub fn with_result<T, E>(result: &Result<T, ErrMode<E>>) -> Self {
+ match result {
+ Ok(_) => Self::Success,
+ Err(ErrMode::Backtrack(_)) => Self::Backtrack,
+ Err(ErrMode::Cut(_)) => Self::Cut,
+ Err(ErrMode::Incomplete(_)) => Self::Incomplete,
+ }
+ }
+}
+
+pub fn start<I: Stream>(
+ depth: usize,
+ name: &dyn crate::lib::std::fmt::Display,
+ count: usize,
+ input: &I,
+) {
+ let gutter_style = anstyle::Style::new().bold();
+ let input_style = anstyle::Style::new().underline();
+ let eof_style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Cyan.into()));
+
+ let (call_width, input_width) = column_widths();
+
+ let count = if 0 < count {
+ format!(":{count}")
+ } else {
+ "".to_owned()
+ };
+ let call_column = format!("{:depth$}> {name}{count}", "");
+
+ // The debug version of `slice` might be wider, either due to rendering one byte as two nibbles or
+ // escaping in strings.
+ let mut debug_slice = format!("{:#?}", input.raw());
+ let (debug_slice, eof) = if let Some(debug_offset) = debug_slice
+ .char_indices()
+ .enumerate()
+ .find_map(|(pos, (offset, _))| (input_width <= pos).then_some(offset))
+ {
+ debug_slice.truncate(debug_offset);
+ let eof = "";
+ (debug_slice, eof)
+ } else {
+ let eof = if debug_slice.chars().count() < input_width {
+ "∅"
+ } else {
+ ""
+ };
+ (debug_slice, eof)
+ };
+
+ let writer = anstream::stderr();
+ let mut writer = writer.lock();
+ let _ = writeln!(
+ writer,
+ "{call_column:call_width$} {gutter_style}|{gutter_reset} {input_style}{debug_slice}{input_reset}{eof_style}{eof}{eof_reset}",
+ gutter_style=gutter_style.render(),
+ gutter_reset=gutter_style.render_reset(),
+ input_style=input_style.render(),
+ input_reset=input_style.render_reset(),
+ eof_style=eof_style.render(),
+ eof_reset=eof_style.render_reset(),
+ );
+}
+
+pub fn end(
+ depth: usize,
+ name: &dyn crate::lib::std::fmt::Display,
+ count: usize,
+ consumed: usize,
+ severity: Severity,
+) {
+ let gutter_style = anstyle::Style::new().bold();
+
+ let (call_width, _) = column_widths();
+
+ let count = if 0 < count {
+ format!(":{count}")
+ } else {
+ "".to_owned()
+ };
+ let call_column = format!("{:depth$}< {name}{count}", "");
+
+ let (status_style, status) = match severity {
+ Severity::Success => {
+ let style = anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into()));
+ let status = format!("+{}", consumed);
+ (style, status)
+ }
+ Severity::Backtrack => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
+ "backtrack".to_owned(),
+ ),
+ Severity::Cut => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
+ "cut".to_owned(),
+ ),
+ Severity::Incomplete => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
+ "incomplete".to_owned(),
+ ),
+ };
+
+ let writer = anstream::stderr();
+ let mut writer = writer.lock();
+ let _ = writeln!(
+ writer,
+ "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
+ gutter_style=gutter_style.render(),
+ gutter_reset=gutter_style.render_reset(),
+ status_style=status_style.render(),
+ status_reset=status_style.render_reset(),
+ );
+}
+
+pub fn result(depth: usize, name: &dyn crate::lib::std::fmt::Display, severity: Severity) {
+ let gutter_style = anstyle::Style::new().bold();
+
+ let (call_width, _) = column_widths();
+
+ let call_column = format!("{:depth$}| {name}", "");
+
+ let (status_style, status) = match severity {
+ Severity::Success => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Green.into())),
+ "",
+ ),
+ Severity::Backtrack => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Yellow.into())),
+ "backtrack",
+ ),
+ Severity::Cut => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
+ "cut",
+ ),
+ Severity::Incomplete => (
+ anstyle::Style::new().fg_color(Some(anstyle::AnsiColor::Red.into())),
+ "incomplete",
+ ),
+ };
+
+ let writer = anstream::stderr();
+ let mut writer = writer.lock();
+ let _ = writeln!(
+ writer,
+ "{status_style}{call_column:call_width$}{status_reset} {gutter_style}|{gutter_reset} {status_style}{status}{status_reset}",
+ gutter_style=gutter_style.render(),
+ gutter_reset=gutter_style.render_reset(),
+ status_style=status_style.render(),
+ status_reset=status_style.render_reset(),
+ );
+}
+
+fn column_widths() -> (usize, usize) {
+ let term_width = term_width();
+
+ let min_call_width = 40;
+ let min_input_width = 20;
+ let decor_width = 3;
+ let extra_width = term_width
+ .checked_sub(min_call_width + min_input_width + decor_width)
+ .unwrap_or_default();
+ let call_width = min_call_width + 2 * extra_width / 3;
+ let input_width = min_input_width + extra_width / 3;
+
+ (call_width, input_width)
+}
+
+fn term_width() -> usize {
+ columns_env().or_else(query_width).unwrap_or(80)
+}
+
+fn query_width() -> Option<usize> {
+ use is_terminal::IsTerminal;
+ if std::io::stderr().is_terminal() {
+ terminal_size::terminal_size().map(|(w, _h)| w.0.into())
+ } else {
+ None
+ }
+}
+
+fn columns_env() -> Option<usize> {
+ std::env::var("COLUMNS")
+ .ok()
+ .and_then(|c| c.parse::<usize>().ok())
+}
diff --git a/src/trace/mod.rs b/src/trace/mod.rs
new file mode 100644
index 0000000..316733e
--- /dev/null
+++ b/src/trace/mod.rs
@@ -0,0 +1,112 @@
+//! 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<I: Stream, O, E>(
+ name: impl crate::lib::std::fmt::Display,
+ mut parser: impl Parser<I, O, E>,
+) -> impl Parser<I, O, E> {
+ #[cfg(feature = "debug")]
+ {
+ let mut call_count = 0;
+ move |i: &mut I| {
+ let depth = internals::Depth::new();
+ let original = i.checkpoint();
+ internals::start(*depth, &name, call_count, i);
+
+ let res = parser.parse_next(i);
+
+ let consumed = i.offset_from(&original);
+ let severity = internals::Severity::with_result(&res);
+ internals::end(*depth, &name, call_count, consumed, severity);
+ call_count += 1;
+
+ res
+ }
+ }
+ #[cfg(not(feature = "debug"))]
+ {
+ parser
+ }
+}
+
+#[cfg_attr(not(feature = "debug"), allow(unused_variables))]
+pub(crate) fn trace_result<T, E>(
+ name: impl crate::lib::std::fmt::Display,
+ res: &Result<T, ErrMode<E>>,
+) {
+ #[cfg(feature = "debug")]
+ {
+ let depth = internals::Depth::existing();
+ let severity = internals::Severity::with_result(res);
+ internals::result(*depth, &name, severity);
+ }
+}
+
+#[test]
+#[cfg(feature = "std")]
+#[cfg_attr(miri, ignore)]
+#[cfg(unix)]
+#[cfg(feature = "debug")]
+fn example() {
+ use term_transcript::{test::TestConfig, ShellOptions};
+
+ let path = snapbox::cmd::compile_example("string", ["--features=debug"]).unwrap();
+
+ let current_dir = path.parent().unwrap();
+ let cmd = path.file_name().unwrap();
+ // HACK: term_transcript doesn't allow non-UTF8 paths
+ let cmd = format!("./{}", cmd.to_string_lossy());
+
+ TestConfig::new(
+ ShellOptions::default()
+ .with_current_dir(current_dir)
+ .with_env("CLICOLOR_FORCE", "1"),
+ )
+ .test("assets/trace.svg", [cmd.as_str()]);
+}