@ 261104.1

261028.3.1 · 23815624819c77ab665e19c8e6c28bddbcd96e1d

refactor: different pattern types

This significantly refactors the code for creating the regexes to match with, and in some cases avoids using regex altogether.

We now have differnt types of patterns that we match with.

One impact of this is that we now use GlobSet to match directly, which should fix #2067 and allow globs to work correctly in windows with --full-path.

reading declarations

CHANGELOG.md

@@ -12,6 +12,7 @@ - Fix the `cargo binstall` release URL, which named the crate (`fd-find`) instead of the binary (`fd`) and fell back to a third-party mirror - Fix integer overflow when parsing size constraints, rejecting values that exceed `u64::MAX` instead of panicking, see #2196 (@parneetsingh022). - Various fixes and updates to zsh completion+- Fix --glob patterns when using --full-path on windows. See #2067.  # 10.5.0 

Cargo.lock

@@ -28,9 +28,9 @@ dependencies = [  [[package]] name = "anstyle"-version = "1.0.13"+version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"  [[package]] name = "anstyle-parse"@@ -47,7 +47,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -58,7 +58,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [  "anstyle",  "once_cell_polyfill",- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -86,9 +86,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"  [[package]] name = "bitflags"-version = "2.11.0"+version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"+checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"  [[package]] name = "block2"@@ -101,19 +101,19 @@ dependencies = [  [[package]] name = "bstr"-version = "1.12.1"+version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"+checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [  "memchr",- "serde",+ "serde_core", ]  [[package]] name = "cc"-version = "1.2.56"+version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"+checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" dependencies = [  "find-msvc-tools",  "shlex",@@ -121,21 +121,21 @@ dependencies = [  [[package]] name = "cfg-if"-version = "1.0.4"+version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"+checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"  [[package]] name = "cfg_aliases"-version = "0.2.1"+version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"  [[package]] name = "clap"-version = "4.6.1"+version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"+checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" dependencies = [  "clap_builder",  "clap_derive",@@ -143,9 +143,9 @@ dependencies = [  [[package]] name = "clap_builder"-version = "4.6.0"+version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"+checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" dependencies = [  "anstream",  "anstyle",@@ -156,36 +156,36 @@ dependencies = [  [[package]] name = "clap_complete"-version = "4.6.9"+version = "4.6.11" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "3be2ad0423bdbbb0e25bc89add796f3559706d4a95e1bc98e4d9662a957b6a19"+checksum = "037e2a1a92236d0aff7e845093f64661d6df4c02c9fcc61a60e9e1d736fa392f" dependencies = [  "clap", ]  [[package]] name = "clap_derive"-version = "4.6.1"+version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"+checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" dependencies = [  "heck",  "proc-macro2",  "quote",- "syn 2.0.117",+ "syn 3.0.9", ]  [[package]] name = "clap_lex"-version = "1.0.0"+version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"+checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"  [[package]] name = "colorchoice"-version = "1.0.4"+version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"  [[package]] name = "crossbeam-channel"@@ -198,9 +198,9 @@ dependencies = [  [[package]] name = "crossbeam-deque"-version = "0.8.6"+version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"+checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [  "crossbeam-epoch",  "crossbeam-utils",@@ -208,18 +208,18 @@ dependencies = [  [[package]] name = "crossbeam-epoch"-version = "0.9.18"+version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"+checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [  "crossbeam-utils", ]  [[package]] name = "crossbeam-utils"-version = "0.8.21"+version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"+checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"  [[package]] name = "ctrlc"@@ -229,14 +229,14 @@ checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" dependencies = [  "dispatch2",  "nix 0.31.3",- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]] name = "defmt"-version = "1.1.0"+version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f"+checksum = "e4a0795ac740cb54d7b3775c9c17345f6aaecf884c01733b39d6017738e0e3e1" dependencies = [  "bitflags 1.3.2",  "defmt-macros",@@ -244,22 +244,21 @@ dependencies = [  [[package]] name = "defmt-macros"-version = "1.1.0"+version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b"+checksum = "840bb056bd99c495c9188198a258ab787c79f306d864db3e6e8451442d6d8366" dependencies = [  "defmt-parser",- "proc-macro-error2",  "proc-macro2",  "quote",- "syn 2.0.117",+ "syn 3.0.9", ]  [[package]] name = "defmt-parser"-version = "1.0.0"+version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"+checksum = "71958f69fb2de856c5dd51ac9377e986ca1aa5919f931bf5395a807277f889a0" dependencies = [  "thiserror", ]@@ -276,18 +275,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [- "bitflags 2.11.0",+ "bitflags 2.13.2",  "block2",  "libc",  "objc2", ] -[[package]]-name = "equivalent"-version = "1.0.2"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"- [[package]] name = "errno" version = "0.3.14"@@ -295,7 +288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [  "libc",- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -305,7 +298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" dependencies = [  "cfg-if",- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -321,9 +314,9 @@ dependencies = [  [[package]] name = "fastrand"-version = "2.3.0"+version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"  [[package]] name = "fd-find"@@ -345,6 +338,7 @@ dependencies = [  "jiff",  "libc",  "lscolors",+ "memchr",  "nix 0.31.3",  "normpath",  "nu-ansi-term",@@ -367,34 +361,26 @@ dependencies = [  [[package]] name = "find-msvc-tools"-version = "0.1.9"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"--[[package]]-name = "foldhash"-version = "0.1.5"+version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"+checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48"  [[package]] name = "getrandom"-version = "0.4.2"+version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [  "cfg-if",  "libc",  "r-efi",- "wasip2",- "wasip3", ]  [[package]] name = "globset"-version = "0.4.20"+version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988"+checksum = "f98b0d85522dc0a824cfebbc9594ff4a9b289c568bea0370319dfbdf20ccdd2d" dependencies = [  "aho-corasick",  "bstr",@@ -403,38 +389,17 @@ dependencies = [  "regex-syntax", ] -[[package]]-name = "hashbrown"-version = "0.15.5"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"-dependencies = [- "foldhash",-]--[[package]]-name = "hashbrown"-version = "0.16.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"- [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]]-name = "id-arena"-version = "2.3.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"- [[package]] name = "ignore"-version = "0.4.33"+version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f"+checksum = "484be5657551d7868d67587df967fb4fcbe9d266a761cc855aaa148fda0b3cc7" dependencies = [  "crossbeam-deque",  "globset",@@ -446,35 +411,17 @@ dependencies = [  "winapi-util", ] -[[package]]-name = "indexmap"-version = "2.13.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"-dependencies = [- "equivalent",- "hashbrown 0.16.1",- "serde",- "serde_core",-]- [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]]-name = "itoa"-version = "1.0.17"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"- [[package]] name = "jiff"-version = "0.2.37"+version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb"+checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" dependencies = [  "defmt",  "jiff-core",@@ -489,30 +436,31 @@ dependencies = [  [[package]] name = "jiff-core"-version = "0.1.0"+version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"+checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" dependencies = [  "defmt",+ "log", ]  [[package]] name = "jiff-static"-version = "0.2.37"+version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212"+checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" dependencies = [  "jiff-core",  "proc-macro2",  "quote",- "syn 2.0.117",+ "syn 2.0.119", ]  [[package]] name = "jiff-tzdb"-version = "0.1.6"+version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076"+checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7"  [[package]] name = "jiff-tzdb-platform"@@ -523,17 +471,11 @@ dependencies = [  "jiff-tzdb", ] -[[package]]-name = "leb128fmt"-version = "0.1.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"- [[package]] name = "libc"-version = "0.2.189"+version = "0.2.191" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"+checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"  [[package]] name = "linux-raw-sys"@@ -543,9 +485,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"  [[package]] name = "log"-version = "0.4.29"+version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"  [[package]] name = "lscolors"@@ -559,9 +501,9 @@ dependencies = [  [[package]] name = "memchr"-version = "2.8.0"+version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"  [[package]] name = "nix"@@ -569,7 +511,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [- "bitflags 2.11.0",+ "bitflags 2.13.2",  "cfg-if",  "cfg_aliases",  "libc",@@ -581,7 +523,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [- "bitflags 2.11.0",+ "bitflags 2.13.2",  "cfg-if",  "cfg_aliases",  "libc",@@ -593,7 +535,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b11ce00d2594068e8a27c9146fdc5cf9f3ac38eb42c7cd34d05fea618873ac9f" dependencies = [- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -602,14 +544,14 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]] name = "objc2"-version = "0.6.4"+version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"+checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" dependencies = [  "objc2-encode", ]@@ -622,9 +564,9 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"  [[package]] name = "once_cell"-version = "1.21.3"+version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"  [[package]] name = "once_cell_polyfill"@@ -634,65 +576,33 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"  [[package]] name = "portable-atomic"-version = "1.13.1"+version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"+checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"  [[package]] name = "portable-atomic-util"-version = "0.2.5"+version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5"+checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" dependencies = [  "portable-atomic", ] -[[package]]-name = "prettyplease"-version = "0.2.37"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"-dependencies = [- "proc-macro2",- "syn 2.0.117",-]--[[package]]-name = "proc-macro-error-attr2"-version = "2.0.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"-dependencies = [- "proc-macro2",- "quote",-]--[[package]]-name = "proc-macro-error2"-version = "2.0.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"-dependencies = [- "proc-macro-error-attr2",- "proc-macro2",- "quote",- "syn 2.0.117",-]- [[package]] name = "proc-macro2"-version = "1.0.106"+version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [  "unicode-ident", ]  [[package]] name = "quote"-version = "1.0.45"+version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [  "proc-macro2", ]@@ -734,15 +644,15 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"  [[package]] name = "rustix"-version = "1.1.4"+version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"+checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa" dependencies = [- "bitflags 2.11.0",+ "bitflags 2.13.2",  "errno",  "libc",  "linux-raw-sys",- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -754,59 +664,31 @@ dependencies = [  "winapi-util", ] -[[package]]-name = "semver"-version = "1.0.27"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"--[[package]]-name = "serde"-version = "1.0.228"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"-dependencies = [- "serde_core",-]- [[package]] name = "serde_core"-version = "1.0.228"+version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [  "serde_derive", ]  [[package]] name = "serde_derive"-version = "1.0.228"+version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [  "proc-macro2",  "quote",- "syn 2.0.117",-]--[[package]]-name = "serde_json"-version = "1.0.149"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"-dependencies = [- "itoa",- "memchr",- "serde",- "serde_core",- "zmij",+ "syn 3.0.9", ]  [[package]] name = "shlex"-version = "1.3.0"+version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"  [[package]] name = "strsim"@@ -816,9 +698,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"  [[package]] name = "syn"-version = "2.0.117"+version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [  "proc-macro2",  "quote",@@ -827,9 +709,9 @@ dependencies = [  [[package]] name = "syn"-version = "3.0.6"+version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"+checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" dependencies = [  "proc-macro2",  "quote",@@ -846,17 +728,17 @@ dependencies = [  "getrandom",  "once_cell",  "rustix",- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]] name = "terminal_size"-version = "0.4.3"+version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"+checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [  "rustix",- "windows-sys 0.60.2",+ "windows-sys", ]  [[package]]@@ -877,7 +759,7 @@ dependencies = [  "cfg-if",  "proc-macro2",  "quote",- "syn 3.0.6",+ "syn 3.0.9", ]  [[package]]@@ -888,28 +770,28 @@ checksum = "f4ce35ece947bccac166e1ded639133827a953f69a48ba134d17f91108333082" dependencies = [  "proc-macro2",  "quote",- "syn 3.0.6",+ "syn 3.0.9",  "test-case-core", ]  [[package]] name = "thiserror"-version = "2.0.18"+version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"+checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [  "thiserror-impl", ]  [[package]] name = "thiserror-impl"-version = "2.0.18"+version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"+checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [  "proc-macro2",  "quote",- "syn 2.0.117",+ "syn 3.0.9", ]  [[package]]@@ -934,15 +816,9 @@ dependencies = [  [[package]] name = "unicode-ident"-version = "1.0.24"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"--[[package]]-name = "unicode-xid"-version = "0.2.6"+version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"+checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"  [[package]] name = "utf8parse"@@ -960,58 +836,6 @@ dependencies = [  "winapi-util", ] -[[package]]-name = "wasip2"-version = "1.0.2+wasi-0.2.9"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"-dependencies = [- "wit-bindgen",-]--[[package]]-name = "wasip3"-version = "0.4.0+wasi-0.3.0-rc-2026-01-06"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"-dependencies = [- "wit-bindgen",-]--[[package]]-name = "wasm-encoder"-version = "0.244.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"-dependencies = [- "leb128fmt",- "wasmparser",-]--[[package]]-name = "wasm-metadata"-version = "0.244.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"-dependencies = [- "anyhow",- "indexmap",- "wasm-encoder",- "wasmparser",-]--[[package]]-name = "wasmparser"-version = "0.244.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"-dependencies = [- "bitflags 2.11.0",- "hashbrown 0.15.5",- "indexmap",- "semver",-]- [[package]] name = "winapi" version = "0.3.9"@@ -1034,7 +858,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [- "windows-sys 0.61.2",+ "windows-sys", ]  [[package]]@@ -1049,15 +873,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]]-name = "windows-sys"-version = "0.60.2"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"-dependencies = [- "windows-targets",-]- [[package]] name = "windows-sys" version = "0.61.2"@@ -1066,162 +881,3 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [  "windows-link", ]--[[package]]-name = "windows-targets"-version = "0.53.5"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"-dependencies = [- "windows-link",- "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.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"--[[package]]-name = "windows_aarch64_msvc"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"--[[package]]-name = "windows_i686_gnu"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"--[[package]]-name = "windows_i686_gnullvm"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"--[[package]]-name = "windows_i686_msvc"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"--[[package]]-name = "windows_x86_64_gnu"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"--[[package]]-name = "windows_x86_64_gnullvm"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"--[[package]]-name = "windows_x86_64_msvc"-version = "0.53.1"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"--[[package]]-name = "wit-bindgen"-version = "0.51.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"-dependencies = [- "wit-bindgen-rust-macro",-]--[[package]]-name = "wit-bindgen-core"-version = "0.51.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"-dependencies = [- "anyhow",- "heck",- "wit-parser",-]--[[package]]-name = "wit-bindgen-rust"-version = "0.51.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"-dependencies = [- "anyhow",- "heck",- "indexmap",- "prettyplease",- "syn 2.0.117",- "wasm-metadata",- "wit-bindgen-core",- "wit-component",-]--[[package]]-name = "wit-bindgen-rust-macro"-version = "0.51.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"-dependencies = [- "anyhow",- "prettyplease",- "proc-macro2",- "quote",- "syn 2.0.117",- "wit-bindgen-core",- "wit-bindgen-rust",-]--[[package]]-name = "wit-component"-version = "0.244.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"-dependencies = [- "anyhow",- "bitflags 2.11.0",- "indexmap",- "log",- "serde",- "serde_derive",- "serde_json",- "wasm-encoder",- "wasm-metadata",- "wasmparser",- "wit-parser",-]--[[package]]-name = "wit-parser"-version = "0.244.0"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"-dependencies = [- "anyhow",- "id-arena",- "indexmap",- "log",- "semver",- "serde",- "serde_derive",- "serde_json",- "unicode-xid",- "wasmparser",-]--[[package]]-name = "zmij"-version = "1.0.21"-source = "registry+https://github.com/rust-lang/crates.io-index"-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

Cargo.toml

@@ -38,7 +38,7 @@ ignore = "0.4.28" regex = "1.12.2" regex-syntax = "0.8" ctrlc = "3.5"-globset = "0.4"+globset = "0.4.19" anyhow = "1.0" etcetera = "0.11" normpath = "1.5.1"@@ -46,6 +46,7 @@ crossbeam-channel = "0.5.15" clap_complete = {version = "4.6.5", optional = true} faccess = "0.2.4" jiff = "0.2.27"+memchr = "2.8.0"  [dependencies.clap] version = "4.6.1"

src/cli.rs

@@ -641,13 +641,11 @@ pub struct Opts {     /// is considered a match. If your pattern starts with a dash (-), make sure to     /// pass '--' first, or it will be considered as a flag (fd -- '-foo').     #[arg(-        default_value = "",-        hide_default_value = true,         value_name = "pattern",         help = "the search pattern (a regular expression, unless '--glob' is used; optional)",         long_help     )]-    pub pattern: String,+    pub pattern: Option<String>,      /// Set the path separator to use when printing file paths. The default is     /// the OS-specific separator ('/' on Unix, '\' on Windows).

src/config.rs

@@ -12,9 +12,6 @@ use crate::fmt::FormatTemplate;  /// Configuration options for *fd*. pub struct Config {-    /// Whether the search is case-sensitive or case-insensitive.-    pub case_sensitive: bool,-     /// Cached current working directory for absolute path construction.     /// Populated when `--full-path` is set; `None` means search by filename only.     pub full_path_base: Option<PathBuf>,

src/filesystem.rs

@@ -1,6 +1,4 @@-use std::borrow::Cow; use std::env;-use std::ffi::OsStr; use std::fs; use std::io; #[cfg(any(unix, target_os = "redox"))]@@ -99,22 +97,6 @@ pub fn is_pipe(_: fs::FileType) -> bool {     false } -#[cfg(any(unix, target_os = "redox"))]-pub fn osstr_to_bytes(input: &OsStr) -> Cow<'_, [u8]> {-    use std::os::unix::ffi::OsStrExt;-    Cow::Borrowed(input.as_bytes())-}--#[cfg(windows)]-pub fn osstr_to_bytes(input: &OsStr) -> Cow<'_, [u8]> {-    let string = input.to_string_lossy();--    match string {-        Cow::Owned(string) => Cow::Owned(string.into_bytes()),-        Cow::Borrowed(string) => Cow::Borrowed(string.as_bytes()),-    }-}- /// Remove the `./` prefix from a path. pub fn strip_current_dir(path: &Path) -> &Path {     path.strip_prefix(".").unwrap_or(path)

src/main.rs

@@ -13,6 +13,7 @@ mod filter; mod fmt; mod hyperlink; mod output;+mod patterns; mod regex_helper; mod sanitize; mod walk;@@ -24,9 +25,9 @@ use std::sync::Arc;  use anyhow::{Context, Result, anyhow, bail}; use clap::{CommandFactory, Parser};-use globset::GlobBuilder; use lscolors::LsColors;-use regex::bytes::{Regex, RegexBuilder, RegexSetBuilder};+use patterns::PatternType;+use regex::bytes::RegexSetBuilder;  use crate::cli::{ColorWhen, HyperlinkWhen, Opts}; use crate::config::Config;@@ -36,6 +37,7 @@ use crate::filetypes::FileTypes; #[cfg(unix)] use crate::filter::OwnerFilter; use crate::filter::TimeFilter;+use crate::patterns::build_patterns; use crate::regex_helper::{pattern_has_uppercase_char, pattern_matches_strings_with_leading_dot};  // We use jemalloc for performance reasons, see https://github.com/sharkdp/fd/pull/481@@ -78,7 +80,7 @@ fn main() { }  fn run() -> Result<ExitCode> {-    let opts = Opts::parse();+    let mut opts = Opts::parse();      #[cfg(feature = "completions")]     if let Some(shell) = opts.gen_completions()? {@@ -92,28 +94,24 @@ fn run() -> Result<ExitCode> {     }      ensure_search_pattern_is_not_a_path(&opts)?;-    let pattern = &opts.pattern;-    let exprs = &opts.exprs;-    let empty = Vec::new();+    let mut patterns = opts.exprs.take().unwrap_or(Vec::new());+    if let Some(pattern) = opts.pattern.take() {+        patterns.push(pattern);+    } -    let pattern_regexps = exprs-        .as_ref()-        .unwrap_or(&empty)-        .iter()-        .chain([pattern])-        .map(|pat| build_pattern_regex(pat, &opts))-        .collect::<Result<Vec<String>>>()?;+    let pattern_type = determine_pattern_type(&opts);+    // The search will be case-sensitive if the command line flag is set or+    // if any of the patterns has an uppercase character (smart case).+    let ignore_case = opts.ignore_case+        || !(opts.case_sensitive || patterns.iter().any(|pat| pattern_has_uppercase_char(pat))); -    let config = construct_config(opts, &pattern_regexps)?;+    let config = construct_config(opts)?; -    ensure_use_hidden_option_for_leading_dot_pattern(&config, &pattern_regexps)?;+    ensure_use_hidden_option_for_leading_dot_pattern(&patterns, &config, pattern_type)?; -    let regexps = pattern_regexps-        .into_iter()-        .map(|pat| build_regex(pat, &config))-        .collect::<Result<Vec<Regex>>>()?;+    let matcher = build_patterns(patterns, pattern_type, ignore_case)?; -    walk::scan(&search_paths, regexps, config)+    walk::scan(&search_paths, matcher, config) }  #[cfg(feature = "completions")]@@ -175,12 +173,13 @@ fn ensure_search_pattern_is_not_a_path(opts: &Opts) -> Result<()> {     if opts.full_path {         return Ok(());     }--    // Check the primary pattern and every `--and` pattern. They are all matched-    // against the file name (see `run`), so a path separator in any of them is-    // the same silent "no results" footgun.-    for pattern in std::iter::once(&opts.pattern).chain(opts.exprs.iter().flatten()) {-        ensure_single_search_pattern_is_not_a_path(pattern)?;+    if let Some(ref pattern) = opts.pattern {+        // Check the primary pattern and every `--and` pattern. They are all matched+        // against the file name (see `run`), so a path separator in any of them is+        // the same silent "no results" footgun.+        for pattern in std::iter::once(pattern).chain(opts.exprs.iter().flatten()) {+            ensure_single_search_pattern_is_not_a_path(pattern)?;+        }     }     Ok(()) }@@ -220,20 +219,16 @@ fn ensure_single_search_pattern_is_not_a_path(pattern: &str) -> Result<()> {     } } -fn build_pattern_regex(pattern: &str, opts: &Opts) -> Result<String> {-    Ok(if opts.glob && !pattern.is_empty() {-        let glob = GlobBuilder::new(pattern).literal_separator(true).build()?;-        glob.regex().to_owned()+fn determine_pattern_type(opts: &Opts) -> PatternType {+    if opts.glob {+        PatternType::Glob     } else if opts.exact {-        // Anchor the escaped pattern so the full filename (or path) must match exactly.-        // Literal. No substring matching.-        format!("^{}$", regex::escape(pattern))+        PatternType::Exact     } else if opts.fixed_strings {-        // Treat pattern as literal string if '--fixed-strings' is used-        regex::escape(pattern)+        PatternType::Fixed     } else {-        String::from(pattern)-    })+        PatternType::Regex+    } }  fn check_path_separator_length(path_separator: Option<&str>) -> Result<()> {@@ -250,15 +245,7 @@ fn check_path_separator_length(path_separator: Option<&str>) -> Result<()> {     } } -fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config> {-    // The search will be case-sensitive if the command line flag is set or-    // if any of the patterns has an uppercase character (smart case).-    let case_sensitive = !opts.ignore_case-        && (opts.case_sensitive-            || pattern_regexps-                .iter()-                .any(|pat| pattern_has_uppercase_char(pat)));-+fn construct_config(mut opts: Opts) -> Result<Config> {     let path_separator = opts         .path_separator         .take()@@ -313,7 +300,6 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config     };      Ok(Config {-        case_sensitive,         full_path_base,         ignore_hidden: !opts.hidden,         read_fdignore: !opts.no_ignore,@@ -498,14 +484,13 @@ fn extract_time_constraints(opts: &Opts) -> Result<Vec<TimeFilter>> { }  fn ensure_use_hidden_option_for_leading_dot_pattern(+    patterns: &[String],     config: &Config,-    pattern_regexps: &[String],+    pattern_type: PatternType, ) -> Result<()> {     if cfg!(unix)         && config.ignore_hidden-        && pattern_regexps-            .iter()-            .any(|pat| pattern_matches_strings_with_leading_dot(pat))+        && patterns_match_strings_with_leading_dots(patterns, pattern_type)     {         Err(anyhow!(             "The pattern(s) seems to only match files with a leading dot, but hidden files are \@@ -517,18 +502,16 @@ fn ensure_use_hidden_option_for_leading_dot_pattern(     } } -fn build_regex(pattern_regex: String, config: &Config) -> Result<regex::bytes::Regex> {-    RegexBuilder::new(&pattern_regex)-        .case_insensitive(!config.case_sensitive)-        .dot_matches_new_line(true)-        .build()-        .map_err(|e| {-            anyhow!(-                "{}\n\nNote: You can search for literal substrings with '--fixed-strings' \-                 or literal strings with '--exact' options (instead of a regular expression). \-                 Alternatively, you can \-                 also use the '--glob' option to match on a glob pattern.",-                e-            )-        })+fn patterns_match_strings_with_leading_dots(+    patterns: &[String],+    pattern_type: PatternType,+) -> bool {+    let mut iter = patterns.iter();+    match pattern_type {+        PatternType::Regex => iter.any(|pat| pattern_matches_strings_with_leading_dot(pat)),+        // fixed strings aren't anchored so always false+        PatternType::Fixed => false,+        // globs and exact just check if it starts with a .+        PatternType::Glob | PatternType::Exact => patterns.iter().any(|pat| pat.starts_with(".")),+    } }

src/patterns.rs

new file mode 100644@@ -0,0 +1,140 @@+use std::ffi::OsStr;++use anyhow::{Result, anyhow};+use globset::{Glob, GlobBuilder, GlobMatcher, GlobSet, GlobSetBuilder};+use memchr::memmem;+use regex::bytes::{RegexSet, RegexSetBuilder};++pub trait Matcher {+    fn matches_path(&self, path: &OsStr) -> bool;+}++pub type Patterns = Box<dyn Matcher + Send + Sync>;++#[derive(Eq, PartialEq, Copy, Clone)]+pub enum PatternType {+    Regex,+    Fixed,+    Glob,+    Exact,+}++impl Matcher for RegexSet {+    fn matches_path(&self, path: &OsStr) -> bool {+        let haystack = path.as_encoded_bytes();+        let matches = self.matches(haystack);+        // Return true if the number of regexes that matched+        // equals the total number of regexes.+        matches.iter().count() == self.len()+    }+}++impl Matcher for GlobSet {+    fn matches_path(&self, path: &OsStr) -> bool {+        self.matches_all(path)+    }+}++/// In the common case a single glob, it is simpler, and+/// faster to just use a single Glob instead of a GlobSet+impl Matcher for GlobMatcher {+    fn matches_path(&self, path: &OsStr) -> bool {+        self.is_match(path)+    }+}++/// Matcher that matches fixed strings+pub struct FixedStrings(pub Vec<String>);++impl Matcher for FixedStrings {+    fn matches_path(&self, path: &OsStr) -> bool {+        let path = path.as_encoded_bytes();+        self.0.iter().all(|f| bytes_contains(path, f.as_bytes()))+    }+}++pub struct ExactStrings(pub Vec<String>);++impl Matcher for ExactStrings {+    fn matches_path(&self, path: &OsStr) -> bool {+        self.0.iter().all(|s| s.as_str() == path)+    }+}++/// Matcher that matches everything+pub struct MatchAll;+impl Matcher for MatchAll {+    fn matches_path(&self, _path: &OsStr) -> bool {+        true+    }+}+pub fn build_patterns(+    mut patterns: Vec<String>,+    pattern_type: PatternType,+    ignore_case: bool,+) -> Result<Patterns> {+    if patterns.is_empty() {+        return Ok(Box::new(MatchAll));+    }+    match pattern_type {+        PatternType::Glob => build_glob_matcher(patterns, ignore_case),+        PatternType::Fixed if !ignore_case => Ok(Box::new(FixedStrings(patterns))),+        PatternType::Exact if !ignore_case => Ok(Box::new(ExactStrings(patterns))),+        typ => {+            // TODO: is there a better way we could handle case insensitive fixed strings?+            if typ == PatternType::Fixed {+                for pattern in patterns.iter_mut() {+                    *pattern = regex::escape(pattern);+                }+            } else if typ == PatternType::Exact {+                // Would using eq_ignore_ascii case be good enough?+                for pattern in patterns.iter_mut() {+                    // Anchor the escaped pattern so the full filename (or path) must match exactly.+                    // Literal. No substring matching.+                    *pattern = format!("^{}$", regex::escape(pattern));+                }+            }+            Ok(Box::new(build_regex_matcher(patterns, ignore_case)?))+        }+    }+}++fn build_glob_matcher(patterns: Vec<String>, ignore_case: bool) -> Result<Patterns> {+    Ok(if patterns.len() == 1 {+        Box::new(build_glob(&patterns[0], ignore_case)?.compile_matcher())+    } else {+        let mut builder = GlobSetBuilder::new();+        for pat in patterns {+            eprintln!("adding glob for {}", pat);+            builder.add(build_glob(&pat, ignore_case)?);+        }+        Box::new(builder.build()?)+    })+}++fn build_glob(pattern: &str, ignore_case: bool) -> Result<Glob> {+    Ok(GlobBuilder::new(pattern)+        .literal_separator(true)+        .case_insensitive(ignore_case)+        .build()?)+}++fn build_regex_matcher(patterns: Vec<String>, ignore_case: bool) -> Result<RegexSet> {+    RegexSetBuilder::new(patterns)+        .case_insensitive(ignore_case)+        .dot_matches_new_line(true)+        .build()+        .map_err(|e| {+            anyhow!(+                "{}\n\nNote: You can search for literal substrings with '--fixed-strings' \+                 or literal strings with '--exact' options (instead of a regular expression). \+                 Alternatively, you can also use the '--glob' option to match on a glob pattern.",+                e,+            )+        })+}++/// Test if the needle is a substring of the haystack+fn bytes_contains(haystack: &[u8], needle: &[u8]) -> bool {+    memmem::find(haystack, needle).is_some()+}

src/walk.rs

@@ -13,14 +13,13 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender, bounded}; use etcetera::BaseStrategy; use ignore::overrides::{Override, OverrideBuilder}; use ignore::{WalkBuilder, WalkParallel, WalkState};-use regex::bytes::Regex;  use crate::config::Config; use crate::dir_entry::DirEntry; use crate::exec; use crate::exit_codes::{ExitCode, merge_exitcodes};-use crate::filesystem; use crate::output;+use crate::patterns::Patterns;  /// The receiver thread can either be buffering results or directly streaming to the console. #[derive(PartialEq)]@@ -304,7 +303,7 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> { /// State shared by the sender and receiver threads. struct WorkerState {     /// The search patterns.-    patterns: Vec<Regex>,+    patterns: Patterns,     /// The command line configuration.     config: Config,     /// Flag for cleanly shutting down the parallel walk@@ -314,7 +313,7 @@ struct WorkerState { }  impl WorkerState {-    fn new(patterns: Vec<Regex>, config: Config) -> Self {+    fn new(patterns: Patterns, config: Config) -> Self {         let quit_flag = Arc::new(AtomicBool::new(false));         let interrupt_flag = Arc::new(AtomicBool::new(false)); @@ -505,17 +504,14 @@ impl WorkerState {                  let search_str = search_str_for_entry(entry_path, config.full_path_base.as_deref()); -                if !patterns-                    .iter()-                    .all(|pat| pat.is_match(&filesystem::osstr_to_bytes(search_str.as_ref())))-                {+                if !patterns.matches_path(&search_str) {                     return WalkState::Continue;                 }                  // Filter out unwanted extensions.                 if let Some(ref exts_regex) = config.extensions {                     if let Some(path_str) = entry_path.file_name() {-                        if !exts_regex.is_match(&filesystem::osstr_to_bytes(path_str)) {+                        if !exts_regex.is_match(path_str.as_encoded_bytes()) {                             return WalkState::Continue;                         }                     } else {@@ -714,7 +710,7 @@ fn search_str_for_entry<'a>( /// If the `--exec` argument was supplied, this will create a thread pool for executing /// jobs in parallel from a given command line and the discovered paths. Otherwise, each /// path will simply be written to standard output.-pub fn scan(paths: &[PathBuf], patterns: Vec<Regex>, config: Config) -> Result<ExitCode> {+pub fn scan(paths: &[PathBuf], patterns: Patterns, config: Config) -> Result<ExitCode> {     WorkerState::new(patterns, config).scan(paths) } 

tests/tests.rs

@@ -616,7 +616,6 @@ fn test_glob_searches() { }  /// Glob-based searches (--glob) in combination with full path searches (--full-path)-#[cfg(not(windows))] // TODO: make this work on Windows #[test] fn test_full_path_glob_searches() {     let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES);