Repository: j178/prefligit (language: python)

Issue:
Rust crate `regex` compatibility issue with Python `re`
`regex` does not supoort negative look-behind? Should we use https://docs.rs/fancy-regex/latest/fancy_regex/ instead?


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index ceb8628..ca6cb08 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -146,6 +146,21 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
 [[package]]
 name = "bitflags"
 version = "2.6.0"
@@ -368,6 +383,17 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
 
+[[package]]
+name = "fancy-regex"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
+dependencies = [
+ "bit-set",
+ "regex-automata 0.4.8",
+ "regex-syntax 0.8.5",
+]
+
 [[package]]
 name = "fastrand"
 version = "2.1.1"
@@ -895,6 +921,7 @@ dependencies = [
  "ctrlc",
  "dunce",
  "etcetera",
+ "fancy-regex",
  "fs-err",
  "fs2",
  "futures",
diff --git a/Cargo.toml b/Cargo.toml
index 7a90266..57d531b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,7 @@ clap = { version = "4.5.16", features = ["derive", "env"] }
 clap_complete = "4.5.37"
 ctrlc = "3.4.5"
 dunce = "1.0.5"
+fancy-regex = "0.14.0"
 fs-err = "2.11.0"
 fs2 = "0.4.3"
 futures = "0.3.31"
@@ -26,7 +27,6 @@ itertools = "0.13.0"
 owo-colors = "4.1.0"
 rand = "0.8.5"
 rayon = "1.10.0"
-regex = "1.11.0"
 rusqlite = { version = "0.32.1", features = ["bundled"] }
 serde = { version = "1.0.210", features = ["derive"] }
 serde_yaml = "0.9.34"
diff --git a/src/languages/fail.rs b/src/languages/fail.rs
index 57cd5f7..0d6489e 100644
--- a/src/languages/fail.rs
+++ b/src/languages/fail.rs
@@ -38,9 +38,9 @@ impl LanguageImpl for Fail {
         out.extend(b"\n\n");
         for f in filenames {
             out.extend(f.as_bytes());
-            out.extend(b"\n");
+            out.push(b'\n');
         }
-        out.extend(b"\n");
+        out.push(b'\n');
 
         Ok((1, out))
     }
diff --git a/src/run.rs b/src/run.rs
index 033d896..2627894 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -7,11 +7,11 @@ use std::path::Path;
 use std::sync::Arc;
 
 use anstream::ColorChoice;
+use fancy_regex::{self as regex, Regex};
 use owo_colors::{OwoColorize, Style};
 use rand::prelude::{SliceRandom, StdRng};
 use rand::SeedableRng;
 use rayon::iter::{IntoParallelIterator, ParallelIterator};
-use regex::Regex;
 use tokio::process::Command;
 use tokio::task::JoinSet;
 use tracing::{error, trace};
@@ -32,7 +32,7 @@ pub struct FilenameFilter {
 }
 
 impl FilenameFilter {
-    pub fn new(include: Option<&str>, exclude: Option<&str>) -> Result<Self, regex::Error> {
+    pub fn new(include: Option<&str>, exclude: Option<&str>) -> Result<Self, Box<regex::Error>> {
         let include = include.map(Regex::new).transpose()?;
         let exclude = exclude.map(Regex::new).transpose()?;
         Ok(Self { include, exclude })
@@ -41,19 +41,19 @@ impl FilenameFilter {
     pub fn filter(&self, filename: impl AsRef<str>) -> bool {
         let filename = filename.as_ref();
         if let Some(re) = &self.include {
-            if !re.is_match(filename) {
+            if !re.is_match(filename).unwrap_or(false) {
                 return false;
             }
         }
         if let Some(re) = &self.exclude {
-            if re.is_match(filename) {
+            if re.is_match(filename).unwrap_or(false) {
                 return false;
             }
         }
         true
     }
 
-    pub fn from_hook(hook: &Hook) -> Result<Self, regex::Error> {
+    pub fn from_hook(hook: &Hook) -> Result<Self, Box<regex::Error>> {
         Self::new(hook.files.as_deref(), hook.exclude.as_deref())
     }
 }


Test patch:
diff --git a/tests/language.rs b/tests/language.rs
index 6732cae..e4b596d 100644
--- a/tests/language.rs
+++ b/tests/language.rs
@@ -6,37 +6,27 @@ use assert_fs::prelude::*;
 mod common;
 
 #[test]
-fn run_fail_language() -> Result<()> {
+fn fail() -> Result<()> {
     let context = TestContext::new();
 
+    context.init_project();
+
     let cwd = context.workdir();
+    cwd.child("changelog").create_dir_all()?;
+    cwd.child("changelog/changelog.md").touch()?;
+
     cwd.child(".pre-commit-config.yaml")
         .write_str(indoc::indoc! {r"
             repos:
-            -   repo: local
+              - repo: local
                 hooks:
-                -   id: changelogs-rst
-                    name: changelogs must be rst
-                    entry: changelog filenames must end in .rst
-                    language: fail
-                    files: 'changelog/.*\.rst$'
+                - id: changelogs-rst
+                  name: changelogs must be rst
+                  entry: changelog filenames must end in .rst
+                  language: fail
+                  files: 'changelog/.*(?<!\.rst)$'
         "})?;
 
-    // Create a repository with some files.
-    let temp_dir = cwd.child("changelog");
-    temp_dir.create_dir_all()?;
-    temp_dir.child("changelog.rst").write_str("changelog")?;
-    temp_dir.child("test.md").write_str("test")?;
-    cwd.child("test.rst").write_str("Hello, world!\n")?;
-    cwd.child("valid.json").write_str("{}")?;
-    cwd.child("invalid.json").write_str("{}")?;
-    cwd.child("main.py").write_str(r#"print "abc"  "#)?;
-
-    Command::new("git")
-        .current_dir(cwd)
-        .arg("init")
-        .assert()
-        .success();
     Command::new("git")
         .current_dir(cwd)
         .arg("add")
@@ -53,7 +43,7 @@ fn run_fail_language() -> Result<()> {
     - exit code: 1
     changelog filenames must end in .rst
 
-    changelog/changelog.rst
+    changelog/changelog.md
 
     ----- stderr -----
     "#);