Skip to main content

compare_changes/
lib.rs

1mod convert;
2mod path;
3
4use std::fmt;
5
6#[derive(Debug)]
7pub enum Error {
8    Parse(Vec<String>), // convert to strings for simplicity
9    Regex(regex::Error),
10}
11
12impl From<regex::Error> for Error {
13    fn from(e: regex::Error) -> Self {
14        Error::Regex(e)
15    }
16}
17
18impl fmt::Display for Error {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            Error::Parse(msgs) => write!(f, "{}", msgs.join("; ")),
22            Error::Regex(e) => write!(f, "{}", e),
23        }
24    }
25}
26
27impl std::error::Error for Error {
28    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
29        match self {
30            Error::Regex(e) => Some(e),
31            _ => None,
32        }
33    }
34}
35
36pub fn path_matches(path: &str, files: &[&str]) -> Result<Option<usize>, Error> {
37    if files.is_empty() {
38        return Ok(None);
39    }
40
41    // Remove leading '?', '+' since that's what GitHub does and also '!' because it's not meaningful when not part of a group
42    let pattern = match path.chars().next() {
43        Some(c) if matches!(c, '?' | '+' | '!') => &path[c.len_utf8()..],
44        _ => path,
45    };
46
47    // Parse the single path — map chumsky Rich errors to strings
48    let parsed_path = match path::parse(pattern) {
49        Ok(p) => p,
50        Err(errs) => {
51            let msgs = errs.into_iter().map(|e| e.to_string()).collect();
52            return Err(Error::Parse(msgs));
53        }
54    };
55
56    // Build regex from the parsed path — propagate compilation errors
57    let re = convert::path_to_regex(&parsed_path)?;
58
59    // Check if any file matches the compiled regex
60    Ok(files.iter().enumerate().find(|(_, file)| re.is_match(file)).map(|(i, _)| i))
61}