Compare commits
10 Commits
5d60552f12
...
4e99dd5057
Author | SHA1 | Date |
---|---|---|
Charles Iliya Krempeaux | 4e99dd5057 | |
Charles Iliya Krempeaux | cd58524dc0 | |
Charles Iliya Krempeaux | 5a1e556549 | |
Charles Iliya Krempeaux | ebd12da522 | |
Charles Iliya Krempeaux | ca42c95e52 | |
Charles Iliya Krempeaux | ebbb49df89 | |
Charles Iliya Krempeaux | 9a424db462 | |
Charles Iliya Krempeaux | 64422ea7ec | |
Charles Iliya Krempeaux | 4b0308c64e | |
Charles Iliya Krempeaux | 7bad8c950e |
|
@ -67,6 +67,8 @@ func CompileTo(target *Pattern, uncompiledPattern string) error {
|
|||
|
||||
target.init(defaultFieldTagName)
|
||||
|
||||
target.template = uncompiledPattern
|
||||
|
||||
s := uncompiledPattern
|
||||
for {
|
||||
index := strings.IndexRune(s, '{')
|
||||
|
|
44
doc.go
44
doc.go
|
@ -1,12 +1,43 @@
|
|||
/*
|
||||
Package pathmatch provides pattern matching for paths.
|
||||
Package pathmatch provides pattern matching for path templates.
|
||||
|
||||
For example, a path could be a file system path, or a path could be a path from a URL (such as an HTTP or HTTPS based URL).
|
||||
A path template might look something like the following:
|
||||
|
||||
The matches can be loaded into variables (when using pathmatch.Find());
|
||||
or can be loaded into a struct (when using pathmatch.Pattern.FindAndLoad()).
|
||||
/v1/users/{user_id}
|
||||
|
||||
Example Usage:
|
||||
Or:
|
||||
|
||||
/account={account_name}/user={user_name}/message={message_hash}
|
||||
|
||||
Or:
|
||||
|
||||
/backup/{folder_name}/
|
||||
Or:
|
||||
|
||||
/v2/select/{fields}/from/{table_name}/where/{filters}
|
||||
|
||||
This path template could be a file system path, or a path could be a path from a URL (such as an HTTP or HTTPS based URL).
|
||||
|
||||
To compile one of these pattern templates, you would do something such as:
|
||||
|
||||
var template string = "/v1/users/{user_id}/messages/{message_id}"
|
||||
|
||||
var pattern pathmatch.Pattern
|
||||
|
||||
err := pathmatch.CompileTo(&pattern, template)
|
||||
if nil != err {
|
||||
fmt.Fprintf(os.Stdout, "ERROR: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
(In addition to the pathmatch.CompileTo() func, there is also the pathmatch.Compile(), and
|
||||
pathmatch.MustCompile(). But pathmatch.CompileTo() is recommended over the other 2 options
|
||||
for most cases.)
|
||||
|
||||
One you have the compiled pattern, you would either use pathmatch.Match(), pathmatch.Find(),
|
||||
or pathmatch.FindAndLoad() depending on what you were trying to accomplish.
|
||||
|
||||
Example Usage
|
||||
|
||||
var pattern pathmatch.Pattern
|
||||
|
||||
|
@ -35,7 +66,7 @@ Example Usage:
|
|||
fmt.Printf("user_id = %q \n", userId) // user_id = "bMM_kJFMEV"
|
||||
fmt.Printf("vehicle_id = %q \n", vehicleId) // vehicle_id = "o_bcU.RZGK"
|
||||
|
||||
Alternate Example Usage:
|
||||
Alternate Example Usage
|
||||
|
||||
var pattern pathmatch.Pattern
|
||||
|
||||
|
@ -64,6 +95,5 @@ Alternate Example Usage:
|
|||
|
||||
fmt.Printf("user_id = %q \n", data.UserId) // user_id = "bMM_kJFMEV"
|
||||
fmt.Printf("vehicle_id = %q \n", data.VehicleId) // vehicle_id = "o_bcU.RZGK"
|
||||
|
||||
*/
|
||||
package pathmatch
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package pathmatch_test
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-pathmatch"
|
||||
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func ExamplePattern_String() {
|
||||
|
||||
var template = "/v1/users/{user_id}"
|
||||
|
||||
var pattern pathmatch.Pattern
|
||||
|
||||
err := pathmatch.CompileTo(&pattern, template)
|
||||
if nil != err {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("pattern: %s", pattern)
|
||||
|
||||
// Output:
|
||||
// pattern: /v1/users/{user_id}
|
||||
}
|
|
@ -32,6 +32,7 @@ import (
|
|||
// }
|
||||
type Pattern struct {
|
||||
mutex sync.RWMutex
|
||||
template string
|
||||
bits []string
|
||||
names []string
|
||||
namesSet map[string]struct{}
|
||||
|
|
|
@ -12,6 +12,11 @@ var (
|
|||
errThisShouldNeverHappen = newInternalError("This should never happen.")
|
||||
)
|
||||
|
||||
// Find compares ‘path’ against its (compiled) pattern template; if it matches it loads the
|
||||
// matches into ‘args’, and then returns true.
|
||||
//
|
||||
// Find may set some, or all of the items in ‘args’ even if it returns false, and even if it
|
||||
// returns an error.
|
||||
func (pattern *Pattern) Find(path string, args ...interface{}) (bool, error) {
|
||||
if nil == pattern {
|
||||
return false, errNilReceiver
|
||||
|
@ -33,23 +38,32 @@ func (pattern *Pattern) Find(path string, args ...interface{}) (bool, error) {
|
|||
|
||||
s = s[len(bit):]
|
||||
case wildcardBit:
|
||||
index := strings.IndexRune(s, '/')
|
||||
if -1 == index {
|
||||
if err := set(s, argsIndex, args...); nil != err {
|
||||
return doesNotMatter, err
|
||||
if "" == s {
|
||||
return false, nil
|
||||
}
|
||||
argsIndex++
|
||||
} else if 0 <= index {
|
||||
value := s[:index]
|
||||
|
||||
index := strings.IndexRune(s, '/')
|
||||
|
||||
var value string
|
||||
switch {
|
||||
default:
|
||||
return doesNotMatter, errThisShouldNeverHappen
|
||||
case -1 == index:
|
||||
value = s
|
||||
case 0 <= index:
|
||||
value = s[:index]
|
||||
}
|
||||
|
||||
if err := set(value, argsIndex, args...); nil != err {
|
||||
return doesNotMatter, err
|
||||
}
|
||||
argsIndex++
|
||||
s = s[index:]
|
||||
} else {
|
||||
return doesNotMatter, errThisShouldNeverHappen
|
||||
s = s[len(value):]
|
||||
}
|
||||
}
|
||||
|
||||
if "" != s {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package pathmatch
|
||||
package pathmatch_test
|
||||
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-pathmatch"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -9,13 +11,13 @@ import (
|
|||
func TestFind(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Pattern *Pattern
|
||||
Pattern *pathmatch.Pattern
|
||||
Args []interface{}
|
||||
Path string
|
||||
ExpectedArgs []string
|
||||
}{
|
||||
{
|
||||
Pattern: MustCompile("/{this}/{that}/{these}/{those}"),
|
||||
Pattern: pathmatch.MustCompile("/{this}/{that}/{these}/{those}"),
|
||||
Args: []interface{}{new(string), new(string), new(string), new(string), },
|
||||
Path: "/apple/banana/cherry/grape",
|
||||
ExpectedArgs: []string{"apple","banana","cherry","grape"},
|
||||
|
@ -24,65 +26,65 @@ func TestFind(t *testing.T) {
|
|||
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}"),
|
||||
Args: []interface{}{new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/"),
|
||||
Args: []interface{}{new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle"),
|
||||
Args: []interface{}{new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/"),
|
||||
Args: []interface{}{new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/DEFAULT"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/DEFAULT"),
|
||||
Args: []interface{}{new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/DEFAULT",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/DEFAULT/"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/DEFAULT/"),
|
||||
Args: []interface{}{new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/DEFAULT/",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}"),
|
||||
Args: []interface{}{new(string), new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/DEFAULT",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij", "DEFAULT"},
|
||||
},
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}/"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}/"),
|
||||
Args: []interface{}{new(string), new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/DEFAULT/",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij", "DEFAULT"},
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}"),
|
||||
Args: []interface{}{new(string), new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/N9Z_tiv7",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij", "N9Z_tiv7"},
|
||||
},
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}/"),
|
||||
Pattern: pathmatch.MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}/"),
|
||||
Args: []interface{}{new(string), new(string), },
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/N9Z_tiv7/",
|
||||
ExpectedArgs: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij", "N9Z_tiv7"},
|
||||
|
|
|
@ -14,7 +14,13 @@ var (
|
|||
)
|
||||
|
||||
|
||||
func (pattern *Pattern) FindAndLoad(path string, strct interface{}) (bool, error) {
|
||||
// FindAndLoad compares ‘path’ against its (compiled) pattern template; if it matches
|
||||
// it loads the matches into ‘dest’, and then returns true.
|
||||
//
|
||||
// ‘dest’ can be a pointer struct, or a pointer to a []string.
|
||||
//
|
||||
// Find may set some, or all of the items or fields in ‘dest’ even if it returns false, and even if it returns an error.
|
||||
func (pattern *Pattern) FindAndLoad(path string, dest interface{}) (bool, error) {
|
||||
if nil == pattern {
|
||||
return false, errNilReceiver
|
||||
}
|
||||
|
@ -39,12 +45,59 @@ func (pattern *Pattern) FindAndLoad(path string, strct interface{}) (bool, error
|
|||
return false, nil
|
||||
}
|
||||
|
||||
reflectedValue := reflect.ValueOf(strct)
|
||||
reflectedValue := reflect.ValueOf(dest)
|
||||
if reflect.Ptr != reflectedValue.Kind() {
|
||||
//@TODO: change error
|
||||
return doesNotMatter, errExpectedAPointerToAStruct
|
||||
}
|
||||
|
||||
reflectedValueElem := reflectedValue.Elem()
|
||||
switch reflectedValueElem.Kind() {
|
||||
case reflect.Slice:
|
||||
var a []string = make([]string, len(args))
|
||||
for i, arg := range args {
|
||||
a[i] = *(arg.(*string))
|
||||
}
|
||||
|
||||
return loadSlice(dest, a...)
|
||||
case reflect.Struct:
|
||||
return pattern.loadStruct(reflectedValueElem, args)
|
||||
default:
|
||||
//@TODO: change error
|
||||
return doesNotMatter, errExpectedAPointerToAStruct
|
||||
}
|
||||
}
|
||||
|
||||
func loadSlice(dest interface{}, matches ...string) (bool, error) {
|
||||
if nil == dest {
|
||||
return false, errNilTarget
|
||||
}
|
||||
|
||||
target, casted := dest.(*[]string)
|
||||
if !casted {
|
||||
//@TODO: CHANGE ERROR! ============================
|
||||
return false, errExpectedAPointerToAStruct
|
||||
}
|
||||
if nil == target {
|
||||
return false, errNilTarget
|
||||
}
|
||||
|
||||
*target = (*target)[:0]
|
||||
for _, match := range matches {
|
||||
*target = append(*target, match)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (pattern *Pattern) loadStruct(reflectedValueElem reflect.Value, args []interface{}) (bool, error) {
|
||||
if nil == pattern {
|
||||
return false, errNilReceiver
|
||||
}
|
||||
|
||||
if reflect.Struct != reflectedValueElem.Kind() {
|
||||
return doesNotMatter, errExpectedAPointerToAStruct
|
||||
}
|
||||
|
||||
reflectedValueElemType := reflectedValueElem.Type()
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@ package pathmatch
|
|||
import (
|
||||
"github.com/fatih/structs"
|
||||
|
||||
"reflect"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func TestFindAndLoad(t *testing.T) {
|
||||
func TestFindAndLoadStrucs(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Pattern *Pattern
|
||||
|
@ -166,3 +168,73 @@ func TestFindAndLoad(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindAndLoadStrings(t *testing.T) {
|
||||
|
||||
tests := []struct{
|
||||
Pattern *Pattern
|
||||
Path string
|
||||
Expected []string
|
||||
}{
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}"),
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij",
|
||||
Expected: []string{"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij"},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/user/{sessionKey}/vehicle/{vehicleIdcode}/"),
|
||||
Path: "/user/76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij/vehicle/DEFAULT/",
|
||||
Expected: []string{
|
||||
"76M6.mXQfgiGSC_YJ5uXSnWUmELbe8OgOm5n.iZ98Ij",
|
||||
"DEFAULT",
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: MustCompile("/{this}/{that}/{these}/{those}"),
|
||||
Path: "/apple/banana/cherry/grape",
|
||||
Expected: []string{
|
||||
"apple",
|
||||
"banana",
|
||||
"cherry",
|
||||
"grape",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
|
||||
var actual []string
|
||||
|
||||
matched, err := test.Pattern.FindAndLoad(test.Path, &actual)
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q", testNumber, err, err)
|
||||
t.Logf("\tPATTERN: %q", test.Pattern)
|
||||
t.Logf("\tPATH: %q", test.Path)
|
||||
continue
|
||||
}
|
||||
if !matched {
|
||||
t.Errorf("For test #%d, expected a match, but it didn't.", testNumber)
|
||||
t.Logf("\tPATTERN: %q", test.Pattern)
|
||||
t.Logf("\tPATH: %q", test.Path)
|
||||
t.Log("\t--")
|
||||
t.Logf("\tMATCHED: %t", matched)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected := test.Expected; !reflect.DeepEqual(expected, actual) {
|
||||
t.Errorf("For test #%d, did not get what was expected.", testNumber)
|
||||
t.Logf("\tPATTERN: %q", test.Pattern)
|
||||
t.Logf("\tPATH: %q", test.Path)
|
||||
t.Log("\t--")
|
||||
t.Logf("\tEXPECTED: %#v", expected)
|
||||
t.Logf("\tACTUAL: %#v", actual)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package pathmatch
|
||||
|
||||
// Match returns true if ‘path’ matches the compiled pattern, else returns false if it doesn't match.
|
||||
func (receiver *Pattern) Match(path string) (bool, error) {
|
||||
if nil == receiver {
|
||||
return false, errNilReceiver
|
||||
}
|
||||
|
||||
//@TODO: Is it a good idea to be dynamically creating this?
|
||||
//@TODO: Also, can the struct fields be put in here directly instead?
|
||||
args := []interface{}{}
|
||||
numNames := len(receiver.MatchNames())
|
||||
for i:=0; i<numNames; i++ {
|
||||
args = append(args, new(string))
|
||||
}
|
||||
|
||||
found, err := receiver.Find(path, args...)
|
||||
if nil != err {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return found, nil
|
||||
}
|
|
@ -0,0 +1,366 @@
|
|||
package pathmatch_test
|
||||
|
||||
import (
|
||||
"github.com/reiver/go-pathmatch"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPatternMatch(t *testing.T) {
|
||||
tests := []struct{
|
||||
Pattern string
|
||||
Path string
|
||||
Expected bool
|
||||
}{
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/help",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/help/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/help/me",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/help/me/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/helping",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/helping/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v2/help",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v2/HELP",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/apple",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/banana",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help",
|
||||
Path: "/v1/cherry",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/help/",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/help",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/help/me",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/help/me/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/helping",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/helping/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v2/help/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v2/HELP/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/apple/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/banana/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/help/",
|
||||
Path: "/v1/cherry/",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "/v1/user/123",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "/v1/user/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "/v1/user/123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "//v1/user/123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "/v1//user/123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "/v1/user//123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "//v1//user/123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "/v1//user//123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "//v1//user//123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}",
|
||||
Path: "//v1//user//123//",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "/v1/user/123/",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "/v1/user/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "/v1/user/123",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "//v1/user/123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "/v1//user/123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "/v1/user//123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "//v1//user/123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "/v1//user//123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "//v1//user//123/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/",
|
||||
Path: "//v1//user//123//",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}",
|
||||
Path: "/v1/user/123/contact/e-mail",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}",
|
||||
Path: "/v1/user/123/contact/e-mail/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}",
|
||||
Path: "/v2/user/123/contact/e-mail",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}",
|
||||
Path: "/v2/user/123/contact/e-mail",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}/",
|
||||
Path: "/v1/user/123/contact/e-mail/",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}/",
|
||||
Path: "/v1/user/123/contact/e-mail",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}/",
|
||||
Path: "/v2/user/123/contact/e-mail/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/user/{user_id}/contact/{contact_type}/",
|
||||
Path: "/v2/user/123/contact/e-mail/",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}",
|
||||
Path: "/v1/company/acme",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}",
|
||||
Path: "/v1/company/acme/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}",
|
||||
Path: "/v2/company/acme",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}",
|
||||
Path: "/v1/user/acme",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}",
|
||||
Path: "/v1/COMPANY/acme",
|
||||
Expected: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}/",
|
||||
Path: "/v1/company/acme/",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}/",
|
||||
Path: "/v1/company/acme",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}/",
|
||||
Path: "/v2/company/acme/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}/",
|
||||
Path: "/v1/user/acme/",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Pattern: "/v1/company/{company_name}/",
|
||||
Path: "/v1/COMPANY/acme/",
|
||||
Expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for testNumber, test := range tests {
|
||||
var pattern pathmatch.Pattern
|
||||
|
||||
if err := pathmatch.CompileTo(&pattern, test.Pattern); nil != err {
|
||||
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q", testNumber, err, err)
|
||||
t.Errorf("\t: PATTERN: %q", test.Pattern)
|
||||
t.Errorf("\t: PATH: %q", test.Path)
|
||||
t.Errorf("\t: EXPECTED: %t", test.Expected)
|
||||
continue
|
||||
}
|
||||
|
||||
matched, err := pattern.Match(test.Path)
|
||||
if nil != err {
|
||||
t.Errorf("For test #%d, did not expect an error, but actually got one: (%T) %q", testNumber, err, err)
|
||||
t.Errorf("\t: PATTERN: %q", test.Pattern)
|
||||
t.Errorf("\t: PATH: %q", test.Path)
|
||||
t.Errorf("\t: EXPECTED: %t", test.Expected)
|
||||
continue
|
||||
}
|
||||
|
||||
if expected, actual := test.Expected, matched; expected != actual {
|
||||
t.Errorf("For test #%d, expected %t, but actually got %t.", testNumber, expected, actual)
|
||||
t.Errorf("\t: PATTERN: %q", test.Pattern)
|
||||
t.Errorf("\t: PATH: %q", test.Path)
|
||||
t.Errorf("\t: EXPECTED: %t", test.Expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package pathmatch
|
||||
|
||||
// String makes pathmatch.Pattern fit the fmt.Stringer interface.
|
||||
//
|
||||
// String returns the (pre-compiled) pattern template.
|
||||
func (receiver Pattern) String() string {
|
||||
return receiver.template
|
||||
}
|
Loading…
Reference in New Issue