<commit_before>#![feature(macro_rules)]

macro_rules! return_if_some(
    ($arg:expr) => (
        match $arg {
            Some(val) => return val,
            None => {},
        }
    );
)

pub fn pathmatch(pattern: &str, pathstring: &str) -> bool
{
    use std::str::Chars;
    let pattern_chars = pattern.chars();
    let mut path_chars = pathstring.chars();
    return pathmatch_impl(pattern_chars, &mut path_chars, false);

    fn pathmatch_impl(mut pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        if char_iter_equals(pattern_chars, "**/", true) {
            // How can I convert Skip<Chars> to Chars to make use of .skip(3) ?
            let mut pattern_chars_copy = pattern_chars;
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            if pathmatch_try(pattern_chars_copy, path_chars, alt_branch_mode) {
                return true;
            }
        }
        loop {
            if char_iter_equals(pattern_chars, "/**", false) && path_chars.peekable().is_empty() {
                return true;
            }
            match pattern_chars.next() {
                None => return path_chars.next().is_none(),
                Some('?') => match path_chars.next() {
                    None | Some('/') => return false,
                    _ => (),
                },
                Some('*') => match pattern_chars.peekable().peek() {
                    Some(&'*') => {
                        pattern_chars.next();
                        return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, true));
                    },
                    _ => return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, false)),
                },
                Some('/') => {
                    return_if_some!(match_exact('/', path_chars.next(), alt_branch_mode));
                    if char_iter_equals(pattern_chars, "**/", true) && pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                        return true;
                    }
                },
                Some('{') => {
                    let mut matched_something = false;
                    loop {
                        if !matched_something && pathmatch_try(pattern_chars, path_chars, true) {
                            matched_something = true;
                        }
                        match skip_alt_branch(&mut pattern_chars) {
                            Some(false) => break,
                            Some(true) => {},
                            None => /* error in pattern */ return false,
                        }
                    }
                    if !matched_something {
                        return false;
                    }
                },
                Some(',') | Some('}') if alt_branch_mode => return true,
                Some(pc) => return_if_some!(match_exact(pc, path_chars.next(), alt_branch_mode)),
            }
        }
    }

    fn pathmatch_try(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        let mut path_chars_copy = path_chars.clone();
        if pathmatch_impl(pattern_chars, &mut path_chars_copy, alt_branch_mode) {
            *path_chars = path_chars_copy;
            return true;
        }
        return false;
    }

    fn skip_alt_branch(pattern_chars: &mut Chars) -> Option<bool>
    {
        loop {
            match pattern_chars.next() {
                Some(',') => return Some(true),
                Some('}') => return Some(false),
                Some('{') => if skip_alt_branch(pattern_chars).is_none() { return None; },
                None => return None,
                _ => {},
            }
        }
    }

    fn match_any(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool, allow_pathsep: bool) -> Option<bool>
    {
        loop {
            if pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                return Some(true);
            }
            match path_chars.next() {
                None => return None,
                Some('/') if !allow_pathsep => return Some(false),
                _ => {},
            }
        }
    }

    fn match_exact(pattern_char: char, path_char: Option<char>, alt_branch_mode: bool) -> Option<bool>
    {
        match path_char {
            None => return Some(false),
            Some(',') | Some('}') if alt_branch_mode => return Some(false),
            Some(x) if x != pattern_char => return Some(false),
            _ => return None,
        }
    }

    fn char_iter_equals(mut char_iter: Chars, needle: &str, startswith: bool) -> bool
    {
        for needle_char in needle.chars() {
            match char_iter.next() {
                Some(x) if x == needle_char => (),
                _ => return false,
            }
        }
        return if startswith { true } else { char_iter.next().is_none() };
    }
}

#[test]
fn pathmatch_test_alt()
{
    assert!(pathmatch("{foo}", "foo"));
    assert!(pathmatch("{foo,bar,baz}", "foo"));
    assert!(pathmatch("{foo,bar,baz}", "bar"));
    assert!(pathmatch("{foo,bar,baz}", "baz"));
    assert!(!pathmatch("{foo,bar,baz}", "qux"));
    assert!(pathmatch("{foo,bar}", "foo"));
    assert!(pathmatch("{foo,bar}", "bar"));
    assert!(pathmatch("{}", ""));
}

#[test]
fn pathmatch_test_collapse()
{<commit_msg>test multiple matches with one call<commit_after>#![feature(macro_rules)]

macro_rules! return_if_some(
    ($arg:expr) => (
        match $arg {
            Some(val) => return val,
            None => {},
        }
    );
)

pub fn pathmatch(pattern: &str, pathstring: &str) -> bool
{
    use std::str::Chars;
    let pattern_chars = pattern.chars();
    let mut path_chars = pathstring.chars();
    return pathmatch_impl(pattern_chars, &mut path_chars, false);

    fn pathmatch_impl(mut pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        if char_iter_equals(pattern_chars, "**/", true) {
            // How can I convert Skip<Chars> to Chars to make use of .skip(3) ?
            let mut pattern_chars_copy = pattern_chars;
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            if pathmatch_try(pattern_chars_copy, path_chars, alt_branch_mode) {
                return true;
            }
        }
        loop {
            if char_iter_equals(pattern_chars, "/**", false) && path_chars.peekable().is_empty() {
                return true;
            }
            match pattern_chars.next() {
                None => return path_chars.next().is_none(),
                Some('?') => match path_chars.next() {
                    None | Some('/') => return false,
                    _ => (),
                },
                Some('*') => match pattern_chars.peekable().peek() {
                    Some(&'*') => {
                        pattern_chars.next();
                        return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, true));
                    },
                    _ => return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, false)),
                },
                Some('/') => {
                    return_if_some!(match_exact('/', path_chars.next(), alt_branch_mode));
                    if char_iter_equals(pattern_chars, "**/", true) && pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                        return true;
                    }
                },
                Some('{') => {
                    let mut matched_something = false;
                    loop {
                        if !matched_something && pathmatch_try(pattern_chars, path_chars, true) {
                            matched_something = true;
                        }
                        match skip_alt_branch(&mut pattern_chars) {
                            Some(false) => break,
                            Some(true) => {},
                            None => /* error in pattern */ return false,
                        }
                    }
                    if !matched_something {
                        return false;
                    }
                },
                Some(',') | Some('}') if alt_branch_mode => return true,
                Some(pc) => return_if_some!(match_exact(pc, path_chars.next(), alt_branch_mode)),
            }
        }
    }

    fn pathmatch_try(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        let mut path_chars_copy = path_chars.clone();
        if pathmatch_impl(pattern_chars, &mut path_chars_copy, alt_branch_mode) {
            *path_chars = path_chars_copy;
            return true;
        }
        return false;
    }

    fn skip_alt_branch(pattern_chars: &mut Chars) -> Option<bool>
    {
        loop {
            match pattern_chars.next() {
                Some(',') => return Some(true),
                Some('}') => return Some(false),
                Some('{') => if skip_alt_branch(pattern_chars).is_none() { return None; },
                None => return None,
                _ => {},
            }
        }
    }

    fn match_any(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool, allow_pathsep: bool) -> Option<bool>
    {
        loop {
            if pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                return Some(true);
            }
            match path_chars.next() {
                None => return None,
                Some('/') if !allow_pathsep => return Some(false),
                _ => {},
            }
        }
    }

    fn match_exact(pattern_char: char, path_char: Option<char>, alt_branch_mode: bool) -> Option<bool>
    {
        match path_char {
            None => return Some(false),
            Some(',') | Some('}') if alt_branch_mode => return Some(false),
            Some(x) if x != pattern_char => return Some(false),
            _ => return None,
        }
    }

    fn char_iter_equals(mut char_iter: Chars, needle: &str, startswith: bool) -> bool
    {
        for needle_char in needle.chars() {
            match char_iter.next() {
                Some(x) if x == needle_char => (),
                _ => return false,
            }
        }
        return if startswith { true } else { char_iter.next().is_none() };
    }
}

#[cfg(test)]
fn assert_pathmatch_many(pattern: &str, paths_yes: &[&str], paths_no: &[&str])
{
    for path in paths_yes.iter() {
        assert!(pathmatch(pattern, *path), "should match: pattern:'{}' path:'{}'", pattern, path);
    }
    for path in paths_no.iter() {
        assert!(!pathmatch(pattern, *path), "must not match: pattern:'{}' path:'{}'", pattern, path);
    }
}

#[test]
fn pathmatch_test_alt_combos()
{
//    assert_pathmatch_many("{foo/**,**/bar}", ["foo", "bar"], ["foobar"]);
    assert_pathmatch_many("a{?,/}c", ["abc", "a/c"], ["ac", "abbc"]);
//    assert_pathmatch_many("{foo/**,bar}baz", ["barbaz", "foo/baz"], ["foobaz"]);
//    assert_pathmatch_many("foo{bar,**/baz}", ["foobar", "foo/baz"], ["foobaz"]);
}

#[test]
fn pathmatch_test_alt()
{
    assert!(pathmatch("{foo}", "foo"));
    assert_pathmatch_many("{foo,bar,baz}", ["foo", "bar", "baz"], ["", "foobarbaz", "qux"]);
    assert_pathmatch_many("{foo,bar}", ["foo", "bar"], ["", "foobar"]);
    assert!(pathmatch("{}", ""));
    assert!(!pathmatch("{}", "{}"));
    assert!(pathmatch("{foo}{bar}", "foobar"));
    assert!(pathmatch("{foo}*{bar}", "foobar"));
    assert!(pathmatch("{foo}*{bar}", "fooXXbar"));
}

#[test]
fn pathmatch_test_collapse()
{
{'fraction_non_alphanumeric': 0.1429061000685401, 'fraction_numerical': 0.00017135023989033586, 'mean_word_length': 1.7472346434455166, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3042364', 'n_tokens_mistral': 3459, 'n_tokens_neox': 3334, 'n_words': 665}