aboutsummaryrefslogtreecommitdiff
path: root/test/tools/vendor/github.com/vbatts/git-validation/validate/rules.go
blob: 38126a4f0766e29b1f69cfffe2a7f49d42bd792e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package validate

import (
	"sort"
	"strings"
	"sync"

	"github.com/vbatts/git-validation/git"
)

var (
	// RegisteredRules are the avaible validation to perform on git commits
	RegisteredRules  = []Rule{}
	registerRuleLock = sync.Mutex{}
)

// RegisterRule includes the Rule in the avaible set to use
func RegisterRule(vr Rule) {
	registerRuleLock.Lock()
	defer registerRuleLock.Unlock()
	RegisteredRules = append(RegisteredRules, vr)
}

// Rule will operate over a provided git.CommitEntry, and return a result.
type Rule struct {
	Name        string // short name for reference in in the `-run=...` flag
	Value       string // value to configure for the rule (i.e. a regexp to check for in the commit message)
	Description string // longer Description for readability
	Run         func(Rule, git.CommitEntry) Result
	Default     bool // whether the registered rule is run by default
}

// Commit processes the given rules on the provided commit, and returns the result set.
func Commit(c git.CommitEntry, rules []Rule) Results {
	results := Results{}
	for _, r := range rules {
		results = append(results, r.Run(r, c))
	}
	return results
}

// Result is the result for a single validation of a commit.
type Result struct {
	CommitEntry git.CommitEntry
	Pass        bool
	Msg         string
}

// Results is a set of results. This is type makes it easy for the following function.
type Results []Result

// PassFail gives a quick over/under of passes and failures of the results in this set
func (vr Results) PassFail() (pass int, fail int) {
	for _, res := range vr {
		if res.Pass {
			pass++
		} else {
			fail++
		}
	}
	return pass, fail
}

// SanitizeFilters takes a comma delimited list and returns the trimmend and
// split (on ",") items in the list
func SanitizeFilters(filtStr string) (filters []string) {
	for _, item := range strings.Split(filtStr, ",") {
		filters = append(filters, strings.TrimSpace(item))
	}
	return
}

// FilterRules takes a set of rules and a list of short names to include, and
// returns the reduced set.  The comparison is case insensitive.
//
// Some `includes` rules have values assigned to them.
// i.e. -run "dco,message_regexp='^JIRA-[0-9]+ [A-Z].*$'"
//
func FilterRules(rules []Rule, includes []string) []Rule {
	ret := []Rule{}

	for _, r := range rules {
		for i := range includes {
			if strings.Contains(includes[i], "=") {
				chunks := strings.SplitN(includes[i], "=", 2)
				if strings.ToLower(r.Name) == strings.ToLower(chunks[0]) {
					// for these rules, the Name won't be unique per se. There may be
					// multiple "regexp=" with different values. We'll need to set the
					// .Value = chunk[1] and ensure r is dup'ed so they don't clobber
					// each other.
					newR := Rule(r)
					newR.Value = chunks[1]
					ret = append(ret, newR)
				}
			} else {
				if strings.ToLower(r.Name) == strings.ToLower(includes[i]) {
					ret = append(ret, r)
				}
			}
		}
	}

	return ret
}

// StringsSliceEqual compares two string arrays for equality
func StringsSliceEqual(a, b []string) bool {
	if !sort.StringsAreSorted(a) {
		sort.Strings(a)
	}
	if !sort.StringsAreSorted(b) {
		sort.Strings(b)
	}
	for i := range b {
		if !StringsSliceContains(a, b[i]) {
			return false
		}
	}
	for i := range a {
		if !StringsSliceContains(b, a[i]) {
			return false
		}
	}
	return true
}

// StringsSliceContains checks for the presence of a word in string array
func StringsSliceContains(a []string, b string) bool {
	if !sort.StringsAreSorted(a) {
		sort.Strings(a)
	}
	i := sort.SearchStrings(a, b)
	return i < len(a) && a[i] == b
}