package gitattributes // Matcher defines a global multi-pattern matcher for gitattributes patterns type Matcher interface { // Match matches patterns in the order of priorities. Match(path []string, attributes []string) (map[string]Attribute, bool) } type MatcherOptions struct{} // NewMatcher constructs a new matcher. Patterns must be given in the order of // increasing priority. That is the most generic settings files first, then the // content of the repo .gitattributes, then content of .gitattributes down the // path. func NewMatcher(stack []MatchAttribute) Matcher { m := &matcher{stack: stack} m.init() return m } type matcher struct { stack []MatchAttribute macros map[string]MatchAttribute } func (m *matcher) init() { m.macros = make(map[string]MatchAttribute) for _, attr := range m.stack { if attr.Pattern == nil { m.macros[attr.Name] = attr } } } // Match matches path against the patterns in gitattributes files and returns // the attributes associated with the path. // // Specific attributes can be specified otherwise all attributes are returned. // // Matched is true if any path was matched to a rule, even if the results map // is empty. func (m *matcher) Match(path []string, attributes []string) (results map[string]Attribute, matched bool) { results = make(map[string]Attribute, len(attributes)) n := len(m.stack) for i := n - 1; i >= 0; i-- { if len(attributes) > 0 && len(attributes) == len(results) { return } pattern := m.stack[i].Pattern if pattern == nil { continue } if match := pattern.Match(path); match { matched = true for _, attr := range m.stack[i].Attributes { if attr.IsSet() { m.expandMacro(attr.Name(), results) } results[attr.Name()] = attr } } } return } func (m *matcher) expandMacro(name string, results map[string]Attribute) bool { if macro, ok := m.macros[name]; ok { for _, attr := range macro.Attributes { results[attr.Name()] = attr } } return false }