diff --git a/internal/regex_helpers.go b/internal/regex_helpers.go index 106fd5b47..7130f21a8 100644 --- a/internal/regex_helpers.go +++ b/internal/regex_helpers.go @@ -3,37 +3,43 @@ package internal import "regexp" // MatchNamedCaptureGroups takes a regular expression and string and returns all of the named capture group results in a map. -// Note: this is only for the first match in the regex. +// This is only for the first match in the regex. Callers shouldn't be providing regexes with multiple capture groups with the same name. func MatchNamedCaptureGroups(regEx *regexp.Regexp, content string) map[string]string { // note: we are looking across all matches and stopping on the first non-empty match. Why? Take the following example: // input: "cool something to match against" pattern: `((?Pmatch) (?Pagainst))?`. Since the pattern is // encapsulated in an optional capture group, there will be results for each character, but the results will match // on nothing. The only "true" match will be at the end ("match against"). allMatches := regEx.FindAllStringSubmatch(content, -1) - for matchIdx, match := range allMatches { + var results map[string]string + for _, match := range allMatches { // fill a candidate results map with named capture group results, accepting empty values, but not groups with // no names - results := make(map[string]string) for nameIdx, name := range regEx.SubexpNames() { - if nameIdx <= len(match) && len(name) > 0 { - results[name] = match[nameIdx] + if nameIdx > len(match) || len(name) == 0 { + continue } + if results == nil { + results = make(map[string]string) + } + results[name] = match[nameIdx] } // note: since we are looking for the first best potential match we should stop when we find the first one // with non-empty results. - if len(results) > 0 { - foundNonEmptyValue := false - for _, value := range results { - if value != "" { - foundNonEmptyValue = true - break - } - } - // return the first non-empty result, or if this is the last match, the results that were found. - if foundNonEmptyValue || matchIdx == len(allMatches)-1 { - return results - } + if !isEmptyMap(results) { + break } } - return nil + return results +} + +func isEmptyMap(m map[string]string) bool { + if len(m) == 0 { + return true + } + for _, value := range m { + if value != "" { + return false + } + } + return true }