This repository has been archived by the owner on Sep 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gps: Move IgnoredRuleset into its own file
- Loading branch information
Showing
4 changed files
with
307 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package pkgtree | ||
|
||
import ( | ||
"sort" | ||
"strings" | ||
|
||
"github.com/armon/go-radix" | ||
) | ||
|
||
// IgnoredRuleset comprises a set of rules for ignoring import paths. It can | ||
// manage both literal and prefix-wildcard matches. | ||
type IgnoredRuleset struct { | ||
t *radix.Tree | ||
} | ||
|
||
// NewIgnoredRuleset processes a set of strings into an IgnoredRuleset. Strings | ||
// that end in "*" are treated as wildcards, where any import path with a | ||
// matching prefix will be ignored. IgnoredRulesets are immutable once created. | ||
// | ||
// Duplicate and redundant (i.e. a literal path that has a prefix of a wildcard | ||
// path) declarations are discarded. Consequently, it is possible that the | ||
// returned IgnoredRuleset may have a smaller Len() than the input slice. | ||
func NewIgnoredRuleset(ig []string) *IgnoredRuleset { | ||
if len(ig) == 0 { | ||
return &IgnoredRuleset{} | ||
} | ||
|
||
ir := &IgnoredRuleset{ | ||
t: radix.New(), | ||
} | ||
|
||
// Sort the list of all the ignores in order to ensure that wildcard | ||
// precedence is recorded correctly in the trie. | ||
sort.Strings(ig) | ||
for _, i := range ig { | ||
// Skip global ignore and empty string. | ||
if i == "*" || i == "" { | ||
continue | ||
} | ||
|
||
_, wildi, has := ir.t.LongestPrefix(i) | ||
// We may not always have a value here, but if we do, then it's a bool. | ||
wild, _ := wildi.(bool) | ||
// Check if it's a wildcard ignore. | ||
if strings.HasSuffix(i, "*") { | ||
// Check if it is ineffectual. | ||
if has && wild { | ||
// Skip ineffectual wildcard ignore. | ||
continue | ||
} | ||
// Create the ignore prefix and insert in the radix tree. | ||
ir.t.Insert(i[:len(i)-1], true) | ||
} else if !has || !wild { | ||
ir.t.Insert(i, false) | ||
} | ||
} | ||
|
||
if ir.t.Len() == 0 { | ||
ir.t = nil | ||
} | ||
|
||
return ir | ||
} | ||
|
||
// IsIgnored indicates whether the provided path should be ignored, according to | ||
// the ruleset. | ||
func (ir *IgnoredRuleset) IsIgnored(path string) bool { | ||
if path == "" || ir == nil || ir.t == nil { | ||
return false | ||
} | ||
|
||
prefix, wildi, has := ir.t.LongestPrefix(path) | ||
return has && (wildi.(bool) || path == prefix) | ||
} | ||
|
||
// Len indicates the number of rules in the ruleset. | ||
func (ir *IgnoredRuleset) Len() int { | ||
if ir == nil || ir.t == nil { | ||
return 0 | ||
} | ||
|
||
return ir.t.Len() | ||
} | ||
|
||
// ToSlice converts the contents of the IgnoredRuleset to a string slice. | ||
// | ||
// This operation is symmetrically dual to NewIgnoredRuleset. | ||
func (ir *IgnoredRuleset) ToSlice() []string { | ||
irlen := ir.Len() | ||
if irlen == 0 { | ||
return nil | ||
} | ||
|
||
items := make([]string, 0, irlen) | ||
ir.t.Walk(func(s string, v interface{}) bool { | ||
if s != "" { | ||
if v.(bool) { | ||
items = append(items, s+"*") | ||
} else { | ||
items = append(items, s) | ||
} | ||
} | ||
return false | ||
}) | ||
|
||
return items | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package pkgtree | ||
|
||
import "testing" | ||
|
||
func TestIgnoredRuleset(t *testing.T) { | ||
type tfixm []struct { | ||
path string | ||
wild bool | ||
} | ||
cases := []struct { | ||
name string | ||
inputs []string | ||
wantInTree tfixm | ||
wantEmptyTree bool | ||
shouldIgnore []string | ||
shouldNotIgnore []string | ||
}{ | ||
{ | ||
name: "only skip global ignore", | ||
inputs: []string{"*"}, | ||
wantEmptyTree: true, | ||
}, | ||
{ | ||
name: "ignores without ignore suffix", | ||
inputs: []string{ | ||
"x/y/z", | ||
"*a/b/c", | ||
"gophers", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "x/y/z", wild: false}, | ||
{path: "*a/b/c", wild: false}, | ||
{path: "gophers", wild: false}, | ||
}, | ||
shouldIgnore: []string{ | ||
"x/y/z", | ||
"gophers", | ||
}, | ||
shouldNotIgnore: []string{ | ||
"x/y/z/q", | ||
"x/y", | ||
"gopher", | ||
"gopherss", | ||
}, | ||
}, | ||
{ | ||
name: "ignores with ignore suffix", | ||
inputs: []string{ | ||
"x/y/z*", | ||
"a/b/c", | ||
"gophers", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "x/y/z", wild: true}, | ||
{path: "a/b/c", wild: false}, | ||
{path: "gophers", wild: false}, | ||
}, | ||
shouldIgnore: []string{ | ||
"x/y/z", | ||
"x/y/zz", | ||
"x/y/z/", | ||
"x/y/z/q", | ||
}, | ||
shouldNotIgnore: []string{ | ||
"x/y", | ||
"gopher", | ||
}, | ||
}, | ||
{ | ||
name: "global ignore with other strings", | ||
inputs: []string{ | ||
"*", | ||
"gophers*", | ||
"x/y/z*", | ||
"a/b/c", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "x/y/z", wild: true}, | ||
{path: "a/b/c", wild: false}, | ||
{path: "gophers", wild: true}, | ||
}, | ||
shouldIgnore: []string{ | ||
"x/y/z", | ||
"x/y/z/", | ||
"x/y/z/q", | ||
"gophers", | ||
"gopherss", | ||
"gophers/foo", | ||
}, | ||
shouldNotIgnore: []string{ | ||
"x/y", | ||
"gopher", | ||
}, | ||
}, | ||
{ | ||
name: "ineffectual ignore with wildcard", | ||
inputs: []string{ | ||
"a/b*", | ||
"a/b/c*", | ||
"a/b/x/y", | ||
"a/c*", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "a/c", wild: true}, | ||
{path: "a/b", wild: true}, | ||
}, | ||
shouldIgnore: []string{ | ||
"a/cb", | ||
}, | ||
shouldNotIgnore: []string{ | ||
"a/", | ||
"a/d", | ||
}, | ||
}, | ||
{ | ||
name: "same path with and without wildcard", | ||
inputs: []string{ | ||
"a/b*", | ||
"a/b", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "a/b", wild: true}, | ||
}, | ||
shouldIgnore: []string{ | ||
"a/b", | ||
"a/bb", | ||
}, | ||
shouldNotIgnore: []string{ | ||
"a/", | ||
"a/d", | ||
}, | ||
}, | ||
{ | ||
name: "empty paths", | ||
inputs: []string{ | ||
"", | ||
"a/b*", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "a/b", wild: true}, | ||
}, | ||
shouldNotIgnore: []string{ | ||
"", | ||
}, | ||
}, | ||
{ | ||
name: "single wildcard", | ||
inputs: []string{ | ||
"a/b*", | ||
}, | ||
wantInTree: tfixm{ | ||
{path: "a/b", wild: true}, | ||
}, | ||
shouldIgnore: []string{ | ||
"a/b/c", | ||
}, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
igm := NewIgnoredRuleset(c.inputs) | ||
f := func(t *testing.T) { | ||
|
||
if c.wantEmptyTree { | ||
if igm.Len() != 0 { | ||
t.Fatalf("wanted empty tree, but had %v elements", igm.Len()) | ||
} | ||
} | ||
|
||
// Check if the wildcard suffix ignores are in the tree. | ||
for _, p := range c.wantInTree { | ||
wildi, has := igm.t.Get(p.path) | ||
if !has { | ||
t.Fatalf("expected %q to be in the tree", p.path) | ||
} else if wildi.(bool) != p.wild { | ||
if p.wild { | ||
t.Fatalf("expected %q to be a wildcard matcher, but it was not", p.path) | ||
} else { | ||
t.Fatalf("expected %q not to be a wildcard matcher, but it was", p.path) | ||
} | ||
} | ||
} | ||
|
||
for _, p := range c.shouldIgnore { | ||
if !igm.IsIgnored(p) { | ||
t.Fatalf("%q should be ignored, but it was not", p) | ||
} | ||
} | ||
for _, p := range c.shouldNotIgnore { | ||
if igm.IsIgnored(p) { | ||
t.Fatalf("%q should not be ignored, but it was", p) | ||
} | ||
} | ||
} | ||
t.Run(c.name, f) | ||
|
||
igm = NewIgnoredRuleset(igm.ToSlice()) | ||
t.Run(c.name+"/inandout", f) | ||
} | ||
} |
Oops, something went wrong.