Skip to content

Commit d9bf419

Browse files
committed
validate: prep for rules that have a value
where needed, some rules may need to pass in a value like for a reguler expression. Signed-off-by: Vincent Batts <[email protected]>
1 parent cc3ab48 commit d9bf419

File tree

2 files changed

+117
-13
lines changed

2 files changed

+117
-13
lines changed

validate/rules.go

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
package validate
22

33
import (
4+
"sort"
45
"strings"
6+
"sync"
57

68
"github.com/vbatts/git-validation/git"
79
)
810

911
var (
1012
// RegisteredRules are the standard validation to perform on git commits
11-
RegisteredRules = []Rule{}
13+
RegisteredRules = []Rule{}
14+
registerRuleLock = sync.Mutex{}
1215
)
1316

1417
// RegisterRule includes the Rule in the avaible set to use
1518
func RegisterRule(vr Rule) {
19+
registerRuleLock.Lock()
20+
defer registerRuleLock.Unlock()
1621
RegisteredRules = append(RegisteredRules, vr)
1722
}
1823

1924
// Rule will operate over a provided git.CommitEntry, and return a result.
2025
type Rule struct {
2126
Name string // short name for reference in in the `-run=...` flag
27+
Value string // value to configure for the rule (i.e. a regexp to check for in the commit message)
2228
Description string // longer Description for readability
2329
Run func(git.CommitEntry) Result
2430
}
@@ -54,28 +60,74 @@ func (vr Results) PassFail() (pass int, fail int) {
5460
return pass, fail
5561
}
5662

57-
// SanitizeFilters takes a comma delimited list and returns the cleaned items in the list
58-
func SanitizeFilters(filt string) (excludes []string) {
59-
60-
for _, item := range strings.Split(filt, ",") {
61-
excludes = append(excludes, strings.TrimSpace(item))
63+
// SanitizeFilters takes a comma delimited list and returns the trimmend and
64+
// split (on ",") items in the list
65+
func SanitizeFilters(filtStr string) (filters []string) {
66+
for _, item := range strings.Split(filtStr, ",") {
67+
filters = append(filters, strings.TrimSpace(item))
6268
}
63-
6469
return
6570
}
6671

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

7281
for _, r := range rules {
73-
for _, e := range excludes {
74-
if strings.ToLower(r.Name) == strings.ToLower(e) {
75-
ret = append(ret, r)
82+
for i := range includes {
83+
if strings.Contains(includes[i], "=") {
84+
chunks := strings.SplitN(includes[i], "=", 2)
85+
if strings.ToLower(r.Name) == strings.ToLower(chunks[0]) {
86+
// for these rules, the Name won't be unique per se. There may be
87+
// multiple "regexp=" with different values. We'll need to set the
88+
// .Value = chunk[1] and ensure r is dup'ed so they don't clobber
89+
// each other.
90+
newR := Rule(r)
91+
newR.Value = chunks[1]
92+
ret = append(ret, newR)
93+
}
94+
} else {
95+
if strings.ToLower(r.Name) == strings.ToLower(includes[i]) {
96+
ret = append(ret, r)
97+
}
7698
}
7799
}
78100
}
79101

80102
return ret
81103
}
104+
105+
// StringsSliceEqual compares two string arrays for equality
106+
func StringsSliceEqual(a, b []string) bool {
107+
if !sort.StringsAreSorted(a) {
108+
sort.Strings(a)
109+
}
110+
if !sort.StringsAreSorted(b) {
111+
sort.Strings(b)
112+
}
113+
for i := range b {
114+
if !StringsSliceContains(a, b[i]) {
115+
return false
116+
}
117+
}
118+
for i := range a {
119+
if !StringsSliceContains(b, a[i]) {
120+
return false
121+
}
122+
}
123+
return true
124+
}
125+
126+
// StringsSliceContains checks for the presence of a word in string array
127+
func StringsSliceContains(a []string, b string) bool {
128+
if !sort.StringsAreSorted(a) {
129+
sort.Strings(a)
130+
}
131+
i := sort.SearchStrings(a, b)
132+
return i < len(a) && a[i] == b
133+
}

validate/rules_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package validate
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestSanitizeRules(t *testing.T) {
8+
set := []struct {
9+
input string
10+
output []string
11+
}{
12+
{
13+
input: "apples, oranges , bananas",
14+
output: []string{"apples", "oranges", "bananas"},
15+
},
16+
{
17+
input: "apples, oranges , bananas, peaches='with cream'",
18+
output: []string{"apples", "oranges", "bananas", "peaches='with cream'"},
19+
},
20+
}
21+
22+
for i := range set {
23+
filt := SanitizeFilters(set[i].input)
24+
if !StringsSliceEqual(filt, set[i].output) {
25+
t.Errorf("expected output like %v, but got %v", set[i].output, filt)
26+
}
27+
}
28+
}
29+
30+
func TestSliceHelpers(t *testing.T) {
31+
set := []struct {
32+
A, B []string
33+
Equal bool
34+
}{
35+
{
36+
A: []string{"apples", "bananas", "oranges", "mango"},
37+
B: []string{"oranges", "bananas", "apples", "mango"},
38+
Equal: true,
39+
},
40+
{
41+
A: []string{"apples", "bananas", "oranges", "mango"},
42+
B: []string{"waffles"},
43+
Equal: false,
44+
},
45+
}
46+
for i := range set {
47+
got := StringsSliceEqual(set[i].A, set[i].B)
48+
if got != set[i].Equal {
49+
t.Errorf("expected %d A and B comparison to be %q, but got %q", i, set[i].Equal, got)
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)