Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Commit

Permalink
gps: Move IgnoredRuleset into its own file
Browse files Browse the repository at this point in the history
  • Loading branch information
sdboyer committed Oct 14, 2017
1 parent b962643 commit 09ee5b7
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 299 deletions.
111 changes: 111 additions & 0 deletions internal/gps/pkgtree/ignored_ruleset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

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
}
204 changes: 204 additions & 0 deletions internal/gps/pkgtree/ignored_ruleset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

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)
}
}
Loading

0 comments on commit 09ee5b7

Please sign in to comment.