Skip to content

Commit

Permalink
module: add a MatchPrefixPatterns function, for matching GOPRIVATE, etc.
Browse files Browse the repository at this point in the history
This CL exposes a new MatchPrefixPatterns function, extracted from
GlobsMatchPath in src/cmd/go/internal/str. Tool authors can use this to
identify non-public modules by matching against GOPRIVATE, as is
explicitly suggested by `go help module-private`.

Fixes golang/go#38725

Change-Id: I5be79b49c2c13f2d68c7421a06747a297f48f21a
Reviewed-on: https://go-review.googlesource.com/c/mod/+/239797
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
  • Loading branch information
findleyr committed Jun 25, 2020
1 parent 859b3ef commit 0b26df4
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
47 changes: 47 additions & 0 deletions module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ package module

import (
"fmt"
"path"
"sort"
"strings"
"unicode"
Expand Down Expand Up @@ -716,3 +717,49 @@ func unescapeString(escaped string) (string, bool) {
}
return string(buf), true
}

// MatchPrefixPatterns reports whether any path prefix of target matches one of
// the glob patterns (as defined by path.Match) in the comma-separated globs
// list. This implements the algorithm used when matching a module path to the
// GOPRIVATE environment variable, as described by 'go help module-private'.
//
// It ignores any empty or malformed patterns in the list.
func MatchPrefixPatterns(globs, target string) bool {
for globs != "" {
// Extract next non-empty glob in comma-separated list.
var glob string
if i := strings.Index(globs, ","); i >= 0 {
glob, globs = globs[:i], globs[i+1:]
} else {
glob, globs = globs, ""
}
if glob == "" {
continue
}

// A glob with N+1 path elements (N slashes) needs to be matched
// against the first N+1 path elements of target,
// which end just before the N+1'th slash.
n := strings.Count(glob, "/")
prefix := target
// Walk target, counting slashes, truncating at the N+1'th slash.
for i := 0; i < len(target); i++ {
if target[i] == '/' {
if n == 0 {
prefix = target[:i]
break
}
n--
}
}
if n > 0 {
// Not enough prefix elements.
continue
}
matched, _ := path.Match(glob, prefix)
if matched {
return true
}
}
return false
}
34 changes: 34 additions & 0 deletions module/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,37 @@ func TestMatchPathMajor(t *testing.T) {
}
}
}

func TestMatchPrefixPatterns(t *testing.T) {
for _, test := range []struct {
globs, target string
want bool
}{
{"*/quote", "rsc.io/quote", true},
{"*/quo", "rsc.io/quote", false},
{"*/quo??", "rsc.io/quote", true},
{"*/quo*", "rsc.io/quote", true},
{"*quo*", "rsc.io/quote", false},
{"rsc.io", "rsc.io/quote", true},
{"*.io", "rsc.io/quote", true},
{"rsc.io/", "rsc.io/quote", false},
{"rsc", "rsc.io/quote", false},
{"rsc*", "rsc.io/quote", true},

{"rsc.io", "rsc.io/quote/v3", true},
{"*/quote", "rsc.io/quote/v3", true},
{"*/quote/*", "rsc.io/quote/v3", true},
{"*/v3", "rsc.io/quote/v3", false},
{"*/*/v3", "rsc.io/quote/v3", true},
{"*/*/*", "rsc.io/quote/v3", true},
{"*/*/*", "rsc.io/quote", false},

{"*/*/*,,", "rsc.io/quote", false},
{"*/*/*,,*/quote", "rsc.io/quote", true},
{",,*/quote", "rsc.io/quote", true},
} {
if got := MatchPrefixPatterns(test.globs, test.target); got != test.want {
t.Errorf("MatchPrefixPatterns(%q, %q) = %t, want %t", test.globs, test.target, got, test.want)
}
}
}

0 comments on commit 0b26df4

Please sign in to comment.