<commit_before>package wildcard

import (
	"regexp"
	"strings"
)

type NameMatcher interface {
	Matched(name string, dir bool) bool
	Recursive() bool
}

func NewNameMatcher(mask string) NameMatcher {
	if mask == "**/" {
		return RecursiveMatcher{}
	}
	dirOnly := strings.HasSuffix(mask, "/")
	if dirOnly {
		mask = mask[:len(mask)-1]
	}
	nameMask := tryRemoveBackslashes(mask)
	if strings.Contains(nameMask, "[") || strings.Contains(nameMask, "]") || strings.Contains(nameMask, "\\") || strings.Contains(nameMask, "?") {
		return ComplexMatcher{maskToRegexp(nameMask), dirOnly}
	}
	// Subversion compatible mask.
	asterisk := strings.Index(nameMask, "*")
	if asterisk < 0 {
		return EqualsMatcher{nameMask, dirOnly}
	} else if strings.Index(mask[asterisk+1:], "*") < 0 {
		return SimpleMatcher{nameMask[:asterisk], nameMask[asterisk+1:], dirOnly}
	}
	return ComplexMatcher{maskToRegexp(nameMask), dirOnly}
}

func maskToRegexp(mask string) *regexp.Regexp {
	// todo: Mask to regexp
	return nil
}

func tryRemoveBackslashes(pattern string) string {
	result := ""
	start := 0
	for true {
		next := strings.Index(pattern[start:], "\\")
		if next == -1 {
			if start < len(pattern) {
				result += pattern[start:]
			}
			break
		}
		next += start
		if next == len(pattern)-1 {
			// Return original string.
			return pattern
		}
		switch pattern[next+1] {
		case ' ':
		case '#':
		case '!':
			result += pattern[start:next]
			start = next + 1
			break
		default:
			return pattern
		}
	}
	return result
}

// Recursive directory matcher like "**".
type RecursiveMatcher struct {
}

func (this RecursiveMatcher) Matched(name string, dir bool) bool {
	return dir
}

func (this RecursiveMatcher) Recursive() bool {
	return true
}

// Simple matcher for mask with only one asterisk.
type SimpleMatcher struct {
	prefix  string
	suffix  string
	dirOnly bool
}
<commit_msg>wildcard: Name matchers<commit_after>package wildcard

import (
	"fmt"
	"regexp"
	"strings"
)

type NameMatcher interface {
	Matched(name string, dir bool) bool
	Recursive() bool
}

func NewNameMatcher(mask string) NameMatcher {
	if mask == "**/" {
		return RecursiveMatcher{}
	}
	dirOnly := strings.HasSuffix(mask, "/")
	if dirOnly {
		mask = mask[:len(mask)-1]
	}
	nameMask := tryRemoveBackslashes(mask)
	if strings.Contains(nameMask, "[") || strings.Contains(nameMask, "]") || strings.Contains(nameMask, "\\") || strings.Contains(nameMask, "?") {
		return ComplexMatcher{maskToRegexp(nameMask), dirOnly}
	}
	// Subversion compatible mask.
	asterisk := strings.Index(nameMask, "*")
	if asterisk < 0 {
		return EqualsMatcher{nameMask, dirOnly}
	} else if strings.Index(mask[asterisk+1:], "*") < 0 {
		return SimpleMatcher{nameMask[:asterisk], nameMask[asterisk+1:], dirOnly}
	}
	return ComplexMatcher{maskToRegexp(nameMask), dirOnly}
}

func maskToRegexp(mask string) *regexp.Regexp {
	expr := ""
	last := 0
	for index, runeValue := range mask {
		switch runeValue {
		case '[', ']', '(', ')', '|', '-':
			expr += regexp.QuoteMeta(mask[last:index])
			expr += string(byte(runeValue))
			last = index + 1
		case '?':
			expr += regexp.QuoteMeta(mask[last:index])
			expr += "."
			last = index + 1
		case '*':
			expr += regexp.QuoteMeta(mask[last:index])
			expr += ".*"
			last = index + 1
		}
	}
	expr += regexp.QuoteMeta(mask[last:])
	// todo: Mask to regexp
	r, _ := regexp.Compile(expr)
	fmt.Println("  mask: " + mask + " -> " + expr)
	return r
}

func tryRemoveBackslashes(pattern string) string {
	result := ""
	start := 0
	for true {
		next := strings.Index(pattern[start:], "\\")
		if next == -1 {
			if start < len(pattern) {
				result += pattern[start:]
			}
			break
		}
		next += start
		if next == len(pattern)-1 {
			// Return original string.
			return pattern
		}
		switch pattern[next+1] {
		case ' ', '#', '!':
			result += pattern[start:next]
			start = next + 1
			break
		default:
			return pattern
		}
	}
	return result
}

// Recursive directory matcher like "**".
type RecursiveMatcher struct {
}

func (this RecursiveMatcher) Matched(name string, dir bool) bool {
	return dir
}

func (this RecursiveMatcher) Recursive() bool {
	return true
}

// Simple matcher for mask with only one asterisk.
type SimpleMatcher struct {
	prefix  string
	suffix  string
	dirOnly bool
}

{'fraction_non_alphanumeric': 0.14821763602251406, 'fraction_numerical': 0.005628517823639775, 'mean_word_length': 3.824660633484163, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 4, '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': '6404807', 'n_tokens_mistral': 1600, 'n_tokens_neox': 1473, 'n_words': 357}