wildmatch-2.6.1/.cargo_vcs_info.json0000644000000001360000000000100130160ustar { "git": { "sha1": "ca6568be7e9cf5b4487bae0ca5c8068bf27e6c38" }, "path_in_vcs": "" }wildmatch-2.6.1/.github/ISSUE_TEMPLATE/bug_report.md000064400000000000000000000006251046102023000200260ustar 00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **Additional context** Add any other context about the problem here. wildmatch-2.6.1/.github/ISSUE_TEMPLATE/feature_request.md000064400000000000000000000011471046102023000210610ustar 00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. wildmatch-2.6.1/.github/workflows/build.yml000064400000000000000000000012121046102023000170210ustar 00000000000000name: Build on: push: branches: [master] tags: - "v*" pull_request: branches: [master] env: CARGO_TERM_COLOR: always jobs: check-links: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Markup Link Checker (mlc) uses: becheran/mlc@v1 build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose - name: Release uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') wildmatch-2.6.1/.gitignore000064400000000000000000000005121046102023000135740ustar 00000000000000# Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk wildmatch-2.6.1/CONTRIBUTING.md000064400000000000000000000002411046102023000140340ustar 00000000000000# Contribution All contributions and comments welcome! Open an issue or create a Pull Request whenever you find a bug or have an idea to improve this crate. wildmatch-2.6.1/Cargo.lock0000644000000401220000000000100107700ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[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 = "clap" version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[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", "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 = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "is-terminal" version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", "windows-sys", ] [[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.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "ntest" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb183f0a1da7a937f672e5ee7b7edb727bf52b8a52d531374ba8ebb9345c0330" dependencies = [ "ntest_test_cases", "ntest_timeout", ] [[package]] name = "ntest_test_cases" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16d0d3f2a488592e5368ebbe996e7f1d44aa13156efad201f5b4d84e150eaa93" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "ntest_timeout" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc7c92f190c97f79b4a332f5e81dcf68c8420af2045c936c9be0bc9de6f63b5" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 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 = "regex" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-lite" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" [[package]] name = "regex-syntax" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "ryu" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[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 = "serde" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", "syn 2.0.41", ] [[package]] name = "serde_json" version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] [[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.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[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 = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", "winnow", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wildmatch" version = "2.6.1" dependencies = [ "criterion", "glob", "ntest", "rand", "regex", "regex-lite", "serde", ] [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 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.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" dependencies = [ "memchr", ] wildmatch-2.6.1/Cargo.toml0000644000000031330000000000100110140ustar # 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 = "2018" name = "wildmatch" version = "2.6.1" authors = ["Armin Becher "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Simple string matching with single- and multi-character wildcard operator." readme = "README.md" keywords = [ "globbing", "matching", "questionmark", "star", "string-matching", ] categories = ["algorithms"] license = "MIT" repository = "https://github.com/becheran/wildmatch" [features] serde = ["dep:serde"] [lib] name = "wildmatch" path = "src/lib.rs" [[bench]] name = "patterns" path = "benches/patterns.rs" harness = false [dependencies.serde] version = "1.0" features = ["derive"] optional = true default-features = false [dev-dependencies.criterion] version = "0.5.1" default-features = false [dev-dependencies.glob] version = "0.3.1" default-features = false [dev-dependencies.ntest] version = "0.9.0" default-features = false [dev-dependencies.rand] version = "0.8.5" [dev-dependencies.regex] version = "1.10.2" default-features = false [dev-dependencies.regex-lite] version = "0.1.5" wildmatch-2.6.1/Cargo.toml.orig000064400000000000000000000016051046102023000144770ustar 00000000000000[package] name = "wildmatch" version = "2.6.1" authors = ["Armin Becher "] edition = "2018" description = "Simple string matching with single- and multi-character wildcard operator." keywords = ["globbing", "matching", "questionmark", "star", "string-matching"] readme = "README.md" license = "MIT" categories = ["algorithms"] repository = "https://github.com/becheran/wildmatch" [dependencies] serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } [dev-dependencies] ntest = { version = "0.9.0", default-features = false } criterion = { version = "0.5.1", default-features = false } regex = { version = "1.10.2", default-features = false } glob = { version = "0.3.1", default-features = false } regex-lite = {version = "0.1.5"} rand = {version = "0.8.5"} [features] serde = ["dep:serde"] [[bench]] name = "patterns" harness = false wildmatch-2.6.1/LICENSE000064400000000000000000000021021046102023000126060ustar 00000000000000MIT License Copyright (c) 2020 Armin Becher 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. wildmatch-2.6.1/README.md000064400000000000000000000046341046102023000130740ustar 00000000000000# wildmatch [![build status](https://github.com/becheran/wildmatch/workflows/Build/badge.svg)](https://github.com/becheran/wildmatch/actions?workflow=Build) [![docs](https://img.shields.io/docsrs/wildmatch/latest)](https://docs.rs/wildmatch/latest/wildmatch/) [![downloads](https://img.shields.io/crates/v/wildmatch.svg?color=orange)](https://crates.io/crates/wildmatch) [![crate](https://badgen.net/crates/d/wildmatch)](https://crates.io/crates/wildmatch) [![license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/license/MIT) [![codecov](https://img.shields.io/codecov/c/github/becheran/wildmatch/master)](https://app.codecov.io/gh/becheran/wildmatch) Match strings against a simple wildcard pattern. Tests a wildcard pattern `p` against an input string `s`. Returns true only when `p` matches the entirety of `s`. See also the example described on [wikipedia](https://en.wikipedia.org/wiki/Matching_wildcards) for matching wildcards. - `?` matches exactly one occurrence of any character. - `*` matches arbitrary many (including zero) occurrences of any character. - No escape characters are defined. Can also be used with a [custom match pattern](https://docs.rs/wildmatch/latest/wildmatch/struct.WildMatchPattern.html) to define own wildcard patterns for single and multi-character matching. For example the pattern `ca?` will match `cat` or `car`. The pattern `https://*` will match all https urls, such as `https://google.de` or `https://github.com/becheran/wildmatch`. The following table shows a performance benchmarks between wildmatch, [regex](https://crates.io/crates/regex),[glob](https://docs.rs/glob/0.3.0/glob/struct.Pattern.html), and the [regex_lite](https://github.com/rust-lang/regex/tree/master/regex-lite) libraries: | Benchmark | wildmatch | regex | glob | regex_lite | ---- | ------------: | ---------: | -------------: | ---------: | compiling/text | **462 ns** | 39,714 ns | 1,470 ns | 13,210 ns | compiling/complex | 190 ns | 153,830 ns | 238 ns | **60 ns** | matching/text | **186 ns** | 4,065 ns | 456 ns | 6,097 ns | matching/complex | **310 ns** | 16,085 ns | 1,426 ns | 3,773 ns The library only depends on the rust [`stdlib`](https://doc.rust-lang.org/std/). See the [documentation](https://docs.rs/wildmatch/latest/wildmatch/) for usage and more examples. wildmatch-2.6.1/benches/patterns.rs000064400000000000000000000077001046102023000154270ustar 00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion}; use glob::Pattern; use regex::Regex; use regex_lite::Regex as RegexLite; use wildmatch::WildMatch; const TEXT: &str = "Lorem ipsum dolor sit amet, \ consetetur sadipscing elitr, sed diam nonumy eirmod tempor \ invidunt ut labore et dolore magna aliquyam erat, sed diam \ voluptua. At vero eos et accusam et justo duo dolores et ea \ rebum. Stet clita kasd gubergren, no sea takimata sanctus est \ Lorem ipsum dolor sit amet."; const FULL_TEXT_PATTERN: &str = TEXT; const FULL_TEXT_REGEX: &str = "^Lorem ipsum dolor sit amet, \ consetetur sadipscing elitr, sed diam nonumy eirmod tempor \ invidunt ut labore et dolore magna aliquyam erat, sed diam \ voluptua\\. At vero eos et accusam et justo duo dolores et ea \ rebum\\. Stet clita kasd gubergren, no sea takimata sanctus est \ Lorem ipsum dolor sit amet\\.$"; const COMPLEX_PATTERN: &str = "Lorem?ipsum*dolore*ea* ?????ata*."; const COMPLEX_REGEX: &str = "^Lorem.ipsum.*dolore.*ea.* .....ata.*\\.$"; const MOST_COMPLEX_PATTERN: &str = "?a*b*?**c?d****?e*f*g*?*h?i*?*?**j*******k"; const MOST_COMPLEX_REGEX: &str = "^.a.*b.*..*.*c.d.*.*.*.*.e.*f.*g.*..*h.i.*..*..*.*j.*.*.*.*.*.*.*k$"; pub fn compiling(c: &mut Criterion) { let mut group = c.benchmark_group("compiling"); group.bench_function("compile text (wildmatch)", |b| { b.iter(|| WildMatch::new(black_box(FULL_TEXT_PATTERN))) }); group.bench_function("compile complex (wildmatch)", |b| { b.iter(|| WildMatch::new(black_box(MOST_COMPLEX_PATTERN))) }); group.bench_function("compile text (regex)", |b| { b.iter(|| Regex::new(black_box(FULL_TEXT_REGEX)).unwrap()) }); group.bench_function("compile complex (regex)", |b| { b.iter(|| Regex::new(black_box(MOST_COMPLEX_REGEX)).unwrap()) }); group.bench_function("compile text (glob)", |b| { b.iter(|| Pattern::new(black_box(FULL_TEXT_PATTERN))) }); group.bench_function("compile complex (glob)", |b| { b.iter(|| Pattern::new(black_box(MOST_COMPLEX_PATTERN))) }); group.bench_function("compile text (regex_lite)", |b| { b.iter(|| RegexLite::new(black_box(FULL_TEXT_PATTERN))) }); group.bench_function("compile complex (regex_lite)", |b| { b.iter(|| RegexLite::new(black_box(MOST_COMPLEX_PATTERN))) }); } pub fn matching(c: &mut Criterion) { let pattern1 = WildMatch::new(FULL_TEXT_PATTERN); let pattern2 = WildMatch::new(COMPLEX_PATTERN); let regex1 = Regex::new(FULL_TEXT_REGEX).unwrap(); let regex2 = Regex::new(COMPLEX_REGEX).unwrap(); let glob1 = Pattern::new(FULL_TEXT_PATTERN).unwrap(); let glob2 = Pattern::new(COMPLEX_PATTERN).unwrap(); let regex_lite1 = RegexLite::new(FULL_TEXT_PATTERN).unwrap(); let regex_lite2 = RegexLite::new(COMPLEX_PATTERN).unwrap(); let mut group = c.benchmark_group("matching"); group.bench_function("match text (wildmatch)", |b| { b.iter(|| pattern1 == black_box(TEXT)) }); group.bench_function("match complex (wildmatch)", |b| { b.iter(|| pattern2 == black_box(TEXT)) }); group.bench_function("match text (regex)", |b| { b.iter(|| regex1.is_match(black_box(TEXT))) }); group.bench_function("match complex (regex)", |b| { b.iter(|| regex2.is_match(black_box(TEXT))) }); group.bench_function("match text (glob)", |b| { b.iter(|| glob1.matches(black_box(TEXT))) }); group.bench_function("match complex (glob)", |b| { b.iter(|| glob2.matches(black_box(TEXT))) }); group.bench_function("match text (regex_lite)", |b| { b.iter(|| regex_lite1.is_match(black_box(TEXT))) }); group.bench_function("match complex (regex_lite)", |b| { b.iter(|| regex_lite2.is_match(black_box(TEXT))) }); } criterion_group!(benches, compiling, matching); criterion_main!(benches); wildmatch-2.6.1/src/lib.rs000064400000000000000000000420221046102023000135110ustar 00000000000000//! Match strings against a simple wildcard pattern. //! Tests a wildcard pattern `p` against an input string `s`. Returns true only when `p` matches the entirety of `s`. //! //! See also the example described on [wikipedia](https://en.wikipedia.org/wiki/Matching_wildcards) for matching wildcards. //! //! No escape characters are defined. //! //! - `?` matches exactly one occurrence of any character. //! - `*` matches arbitrary many (including zero) occurrences of any character. //! //! Examples matching wildcards: //! ``` rust //! # extern crate wildmatch; use wildmatch::WildMatch; //! assert!(WildMatch::new("cat").matches("cat")); //! assert!(WildMatch::new("*cat*").matches("dog_cat_dog")); //! assert!(WildMatch::new("c?t").matches("cat")); //! assert!(WildMatch::new("c?t").matches("cot")); //! ``` //! Examples not matching wildcards: //! ``` rust //! # extern crate wildmatch; use wildmatch::WildMatch; //! assert!(!WildMatch::new("dog").matches("cat")); //! assert!(!WildMatch::new("*d").matches("cat")); //! assert!(!WildMatch::new("????").matches("cat")); //! assert!(!WildMatch::new("?").matches("cat")); //! ``` //! //! You can specify custom `char` values for the single and multi-character //! wildcards. For example, to use `%` as the multi-character wildcard and //! `_` as the single-character wildcard: //! ```rust //! # extern crate wildmatch; use wildmatch::WildMatchPattern; //! assert!(WildMatchPattern::<'%', '_'>::new("%cat%").matches("dog_cat_dog")); //! ``` use std::fmt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// A wildcard matcher using `*` as the multi-character wildcard and `?` as /// the single-character wildcard. pub type WildMatch = WildMatchPattern<'*', '?'>; /// Wildcard matcher used to match strings. /// /// `MULTI_WILDCARD` is the character used to represent a /// multiple-character wildcard (e.g., `*`), and `SINGLE_WILDCARD` is the /// character used to represent a single-character wildcard (e.g., `?`). /// /// # Panics /// /// Panics at compile time if both wildcard characters are identical. /// /// # Examples /// /// ```compile_fail /// # use wildmatch::WildMatchPattern; /// // ❌ Fails to compile: '*' cannot be both wildcards. /// WildMatchPattern::<'*', '*'>::new(""); /// ``` /// /// ```compile_fail /// # use wildmatch::WildMatchPattern; /// // ❌ Fails to compile: '*' cannot be both wildcards. /// WildMatchPattern::<'*', '*'>::new_case_insensitive(""); /// ``` /// /// ``` /// # use wildmatch::WildMatchPattern; /// // ✅ Compiles fine. /// WildMatchPattern::<'*', '?'>::new(""); /// ``` /// /// ``` /// # use wildmatch::WildMatchPattern; /// // ✅ Compiles fine. /// WildMatchPattern::<'*', '?'>::new_case_insensitive(""); /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, PartialOrd, Default)] pub struct WildMatchPattern { pattern: Vec, case_insensitive: bool, } impl fmt::Display for WildMatchPattern { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use std::fmt::Write; for c in &self.pattern { f.write_char(*c)?; } Ok(()) } } impl WildMatchPattern { const WILDCARDS_DIFFER: () = assert!( MULTI_WILDCARD != SINGLE_WILDCARD, "single and multi wildcards cannot be the same" ); /// Constructor with pattern which can be used for matching. pub fn new(pattern: &str) -> WildMatchPattern { #[allow(clippy::let_unit_value)] let _ = Self::WILDCARDS_DIFFER; let mut simplified: Vec = pattern.chars().collect(); let mut new_len = simplified.len(); let mut wildcard_count = 0; for idx in (0..simplified.len()).rev() { if simplified[idx] == MULTI_WILDCARD { wildcard_count += 1; } else { if wildcard_count > 1 { new_len -= wildcard_count - 1; simplified[idx + 1..].rotate_left(wildcard_count - 1); } wildcard_count = 0; } } if wildcard_count > 1 { new_len -= wildcard_count - 1; simplified.rotate_left(wildcard_count - 1); } simplified.truncate(new_len); Self { pattern: simplified, case_insensitive: false, } } /// Constructor with pattern which can be used for matching with case-insensitive comparison. pub fn new_case_insensitive( pattern: &str, ) -> WildMatchPattern { let mut m = Self::new(pattern); m.case_insensitive = true; m } #[deprecated(since = "2.0.0", note = "use `matches` instead")] pub fn is_match(&self, input: &str) -> bool { self.matches(input) } /// Returns true if pattern applies to the given input string pub fn matches(&self, input: &str) -> bool { if self.pattern.is_empty() { return input.is_empty(); } let mut input_chars = input.chars(); let mut pattern_idx = 0; if let Some(mut input_char) = input_chars.next() { const NONE: usize = usize::MAX; let mut start_idx = NONE; let mut matched = "".chars(); loop { if pattern_idx < self.pattern.len() && self.pattern[pattern_idx] == MULTI_WILDCARD { start_idx = pattern_idx; matched = input_chars.clone(); pattern_idx += 1; } else if pattern_idx < self.pattern.len() && (self.pattern[pattern_idx] == SINGLE_WILDCARD || self.pattern[pattern_idx] == input_char || (self.case_insensitive && self.pattern[pattern_idx].to_lowercase().collect::>() == input_char.to_lowercase().collect::>())) { pattern_idx += 1; if let Some(next_char) = input_chars.next() { input_char = next_char; } else { break; } } else if start_idx != NONE { pattern_idx = start_idx + 1; if let Some(next_char) = matched.next() { input_char = next_char; } else { break; } input_chars = matched.clone(); } else { return false; } } } while pattern_idx < self.pattern.len() && self.pattern[pattern_idx] == MULTI_WILDCARD { pattern_idx += 1; } // If we have reached the end of both the pattern and the text, the pattern matches the text. pattern_idx == self.pattern.len() } /// Returns the pattern string. /// N.B. Consecutive multi-wildcards are simplified to a single multi-wildcard. pub fn pattern(&self) -> String { self.pattern.iter().collect::() } /// Returns the pattern string as a slice of chars. pub fn pattern_chars(&self) -> &[char] { &self.pattern } /// Returns if the pattern is case-insensitive. pub fn is_case_insensitive(&self) -> bool { self.case_insensitive } } impl<'a, const MULTI_WILDCARD: char, const SINGLE_WILDCARD: char> PartialEq<&'a str> for WildMatchPattern { fn eq(&self, &other: &&'a str) -> bool { self.matches(other) } } #[cfg(test)] mod tests { use super::*; use ntest::assert_false; use ntest::test_case; use rand::{distributions::Alphanumeric, Rng}; #[test] fn is_match_random() { const PATTERN_LEN: usize = 100; for _ in 0..1_000 { let mut rng = rand::thread_rng(); let mut pattern: String = rand::thread_rng() .sample_iter(&Alphanumeric) .take(PATTERN_LEN) .map(char::from) .collect(); for _ in 0..rng.gen_range(0..15) { let idx = rng.gen_range(0..PATTERN_LEN); pattern.replace_range(idx..idx + 1, "?") } for _ in 0..rng.gen_range(0..15) { let idx = rng.gen_range(0..PATTERN_LEN); pattern.replace_range(idx..idx + 1, "*") } let m = WildMatch::new(&pattern); for pattern_idx in 0..rng.gen_range(0..1_000) { let mut input = pattern.clone(); for (i, c) in pattern.chars().rev().enumerate() { let idx = pattern.len() - i - 1; if c == '?' { let rand_char: String = rand::thread_rng() .sample_iter(&Alphanumeric) .take(1) .map(char::from) .collect(); input.replace_range(idx..idx + 1, &rand_char) } if c == '*' { let rand_char: String = rand::thread_rng() .sample_iter(&Alphanumeric) .take(rng.gen_range(0..15)) .map(char::from) .collect(); input.replace_range(idx..idx + 1, &rand_char) } } assert!( m.matches(&input), "Pattern ({}): {} doesn't match input: {}", pattern_idx, pattern, input ); } } } #[test_case("**")] #[test_case("*")] #[test_case("*?*")] #[test_case("c*")] #[test_case("c?*")] #[test_case("???")] #[test_case("c?t")] #[test_case("cat")] #[test_case("*cat")] #[test_case("cat*")] fn is_match(pattern: &str) { let m = WildMatch::new(pattern); assert!(m.matches("cat")); } #[test_case("CAT", "cat")] #[test_case("CAT", "CAT")] #[test_case("CA?", "Cat")] #[test_case("C*", "cAt")] #[test_case("C?*", "cAT")] #[test_case("C**", "caT")] #[test_case("КОТ", "кот", name = "cyrillic_lower")] #[test_case("КОТ", "КОТ", name = "cyrillic_upper")] #[test_case("КО?", "Кот", name = "cyrillic_mixed1")] #[test_case("К*", "кОт", name = "cyrillic_mixed2")] #[test_case("К?*", "кОТ", name = "cyrillic_mixed3")] #[test_case("К**", "коТ", name = "cyrillic_mixed4")] fn is_match_case_insensitive(pattern: &str, input: &str) { let m = WildMatch::new_case_insensitive(pattern); assert!(m.matches(input)); } #[test_case("*d*")] #[test_case("*d")] #[test_case("d*")] #[test_case("*c")] #[test_case("?")] #[test_case("??")] #[test_case("????")] #[test_case("?????")] #[test_case("*????")] #[test_case("cats")] #[test_case("cat?")] #[test_case("cacat")] #[test_case("cat*dog")] #[test_case("CAT")] fn no_match(pattern: &str) { let m = WildMatch::new(pattern); assert_false!(m.matches("cat")); } #[test_case("1", "")] #[test_case("?", "")] #[test_case("?", "11")] #[test_case("*1?", "123")] #[test_case("*12", "122")] #[test_case("cat?", "wildcats")] #[test_case("cat*", "wildcats")] #[test_case("*x*", "wildcats")] #[test_case("*a", "wildcats")] #[test_case("", "wildcats")] #[test_case(" ", "wildcats")] #[test_case(" ", "\n")] #[test_case(" ", "\t", name = "whitespaceMismatch")] #[test_case("???", "wildcats")] fn no_match_long(pattern: &str, expected: &str) { let m = WildMatch::new(pattern); assert_false!(m.matches(expected)) } #[test_case("*", "")] #[test_case("*", "1")] #[test_case("?", "1")] #[test_case("*121", "12121")] #[test_case("?*3", "111333")] #[test_case("*113", "1113")] #[test_case("*113", "113")] #[test_case("*113", "11113")] #[test_case("*113", "111113")] #[test_case("*???a", "bbbba")] #[test_case("*???a", "bbbbba")] #[test_case("*???a", "bbbbbba")] #[test_case("*o?a*", "foobar")] #[test_case("*ooo?ar", "foooobar")] #[test_case("*o?a*r", "foobar")] #[test_case("*cat*", "d&(*og_cat_dog")] #[test_case("*?*", "d&(*og_cat_dog")] #[test_case("*a*", "d&(*og_cat_dog")] #[test_case("a*b", "a*xb")] #[test_case("*", "*")] #[test_case("*", "?")] #[test_case("?", "?")] #[test_case("wildcats", "wildcats")] #[test_case("wild*cats", "wild?cats")] #[test_case("wi*ca*s", "wildcats")] #[test_case("wi*ca?s", "wildcats")] #[test_case("*o?", "hog_cat_dog")] #[test_case("*o?", "cat_dog")] #[test_case("*at_dog", "cat_dog")] #[test_case(" ", " ")] #[test_case("* ", "\n ")] #[test_case("\n", "\n", name = "special_chars")] #[test_case("*32", "432")] #[test_case("*32", "332")] #[test_case("*332", "332")] #[test_case("*32", "32")] #[test_case("*32", "3232")] #[test_case("*32", "3232332")] #[test_case("*?2", "332")] #[test_case("*?2", "3332")] #[test_case("33*", "333")] #[test_case("da*da*da*", "daaadabadmanda")] #[test_case("*?", "xx")] fn match_long(pattern: &str, expected: &str) { let m = WildMatch::new(pattern); assert!( m.matches(expected), "Expected pattern {} to match {}", pattern, expected ); } #[test] fn complex_pattern() { const TEXT: &str = "Lorem ipsum dolor sit amet, \ consetetur sadipscing elitr, sed diam nonumy eirmod tempor \ invidunt ut labore et dolore magna aliquyam erat, sed diam \ voluptua. At vero eos et accusam et justo duo dolores et ea \ rebum. Stet clita kasd gubergren, no sea takimata sanctus est \ Lorem ipsum dolor sit amet."; const COMPLEX_PATTERN: &str = "Lorem?ipsum*dolore*ea* ?????ata*."; let m = WildMatch::new(COMPLEX_PATTERN); assert!(m.matches(TEXT)); } #[test] fn complex_pattern_alternative_wildcards() { const TEXT: &str = "Lorem ipsum dolor sit amet, \ consetetur sadipscing elitr, sed diam nonumy eirmod tempor \ invidunt ut labore et dolore magna aliquyam erat, sed diam \ voluptua. At vero eos et accusam et justo duo dolores et ea \ rebum. Stet clita kasd gubergren, no sea takimata sanctus est \ Lorem ipsum dolor sit amet."; const COMPLEX_PATTERN: &str = "Lorem_ipsum%dolore%ea% _____ata%."; let m = WildMatchPattern::<'%', '_'>::new(COMPLEX_PATTERN); assert!(m.matches(TEXT)); } #[test] fn compare_via_equal() { let m = WildMatch::new("c?*"); assert!(m == "cat"); assert!(m == "car"); assert!(m != "dog"); } #[test] fn compare_empty() { let m: WildMatch = WildMatch::new(""); assert!(m != "bar"); assert!(m == ""); } #[test] fn compare_default() { let m: WildMatch = Default::default(); assert!(m == ""); assert!(m != "bar"); } #[test] fn compare_wild_match() { assert_eq!(WildMatch::default(), WildMatch::new("")); assert_eq!(WildMatch::new("abc"), WildMatch::new("abc")); assert_eq!(WildMatch::new("a*bc"), WildMatch::new("a*bc")); assert_ne!(WildMatch::new("abc"), WildMatch::new("a*bc")); assert_ne!(WildMatch::new("a*bc"), WildMatch::new("a?bc")); assert_eq!(WildMatch::new("a***c"), WildMatch::new("a*c")); } #[test] fn print_string() { let m = WildMatch::new("Foo/Bar"); assert_eq!("Foo/Bar", m.to_string()); } #[test] fn to_string_f() { let m = WildMatch::new("F"); assert_eq!("F", m.to_string()); } #[test] fn to_string_with_star() { assert_eq!("a*bc", WildMatch::new("a*bc").to_string()); assert_eq!("a*bc", WildMatch::new("a**bc").to_string()); assert_eq!("a*bc*", WildMatch::new("a*bc*").to_string()); } #[test] fn to_string_with_question_sign() { assert_eq!("a?bc", WildMatch::new("a?bc").to_string()); assert_eq!("a??bc", WildMatch::new("a??bc").to_string()); } #[test] fn to_string_empty() { let m = WildMatch::new(""); assert_eq!("", m.to_string()); } }