2016-02-25 22:59:38 +00:00
|
|
|
|
package pathmatch
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
defaultFieldTagName = "match"
|
|
|
|
|
wildcardBit = "{}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2019-06-21 04:40:10 +00:00
|
|
|
|
errMissingEndingRightBraceToMatchBeginningLeftBrace = newPatternSyntaxError(`Missing ending "}" (to match beginning "{").`)
|
|
|
|
|
errSlashInsideOfBraces = newPatternSyntaxError(`"/" inside of "{...}".`)
|
|
|
|
|
errLeftBraceInsideOfBraces = newPatternSyntaxError(`"{" inside of "{...}".`)
|
2016-02-25 22:59:38 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Compile takes an uncompiled pattern, in the form of a Go string (ex: "/users/{userId}/vehicles/{vehicleId}"),
|
|
|
|
|
// and returns a compiled pattern.
|
|
|
|
|
//
|
2016-11-08 21:25:33 +00:00
|
|
|
|
// The compiled pattern can then be used to test if a path matches the pattern it contains.
|
2016-02-25 22:59:38 +00:00
|
|
|
|
//
|
|
|
|
|
// If the uncompiled pattern has a syntax error, Compile returns an error.
|
|
|
|
|
//
|
|
|
|
|
// Example Usage:
|
|
|
|
|
//
|
|
|
|
|
// pattern, err := pathmath.Compile("/users/{user_id}")
|
|
|
|
|
// if nil != err {
|
|
|
|
|
// fmt.Printf("ERROR Compiling: %v\n", err)
|
|
|
|
|
// return
|
|
|
|
|
// }
|
2019-06-21 20:16:25 +00:00
|
|
|
|
func Compile(uncompiledPattern string) (*Pattern, error) {
|
|
|
|
|
|
|
|
|
|
var pattern Pattern
|
|
|
|
|
|
|
|
|
|
err := CompileTo(&pattern, uncompiledPattern)
|
|
|
|
|
if nil != err {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &pattern, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompileTo takes an uncompiled pattern, in the form of a Go string (ex: "/users/{userId}/vehicles/{vehicleId}"),
|
|
|
|
|
// and compiles the pattern to the ‘target’.
|
|
|
|
|
//
|
|
|
|
|
// The compiled pattern can then be used to test if a path matches the pattern it contains.
|
|
|
|
|
//
|
|
|
|
|
// If the uncompiled pattern has a syntax error, Compile returns an error.
|
|
|
|
|
//
|
|
|
|
|
// Example Usage:
|
|
|
|
|
//
|
|
|
|
|
// var pattern patchmatch.Pattern
|
|
|
|
|
//
|
|
|
|
|
// err := pathmath.CompileTo(&pattern, "/users/{user_id}")
|
|
|
|
|
// if nil != err {
|
|
|
|
|
// fmt.Printf("ERROR Compiling: %v\n", err)
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
func CompileTo(target *Pattern, uncompiledPattern string) error {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
if nil == target {
|
|
|
|
|
return errNilTarget
|
|
|
|
|
}
|
2019-06-21 06:37:46 +00:00
|
|
|
|
|
2019-06-21 20:37:53 +00:00
|
|
|
|
target.mutex.Lock()
|
|
|
|
|
defer target.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
target.init(defaultFieldTagName)
|
2016-02-25 22:59:38 +00:00
|
|
|
|
|
2019-06-24 21:06:00 +00:00
|
|
|
|
target.template = uncompiledPattern
|
|
|
|
|
|
2016-02-25 22:59:38 +00:00
|
|
|
|
s := uncompiledPattern
|
|
|
|
|
for {
|
|
|
|
|
index := strings.IndexRune(s, '{')
|
|
|
|
|
if -1 == index {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
target.bits = append(target.bits, s)
|
2016-02-25 22:59:38 +00:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
bit := s[:index]
|
|
|
|
|
if "" != bit { // This is to deal with the case where a {???} is right at the beginning of the uncompiledPattern.
|
2019-06-21 19:49:06 +00:00
|
|
|
|
target.bits = append(target.bits, bit)
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
s = s[1+index:]
|
|
|
|
|
if "" == s {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
index = strings.IndexRune(s, '}')
|
|
|
|
|
if -1 == index {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
return errMissingEndingRightBraceToMatchBeginningLeftBrace
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There should not be a slash ("/") before the ending brace ("}").
|
|
|
|
|
// If there is, it is a syntax error.
|
|
|
|
|
slashIndex := strings.IndexRune(s, '/')
|
|
|
|
|
if -1 != slashIndex && slashIndex <= index {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
return errSlashInsideOfBraces
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There should not be another beginning brace ("{") before the ending brace ("}").
|
|
|
|
|
// If there is, it is a syntax error.
|
|
|
|
|
anotherLeftBraceIndex := strings.IndexRune(s, '{')
|
|
|
|
|
if -1 != anotherLeftBraceIndex && anotherLeftBraceIndex <= index {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
return errLeftBraceInsideOfBraces
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bit = s[:index]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Match names should be unique, within a pattern.
|
2019-06-21 19:49:06 +00:00
|
|
|
|
if _, ok := target.namesSet[bit]; ok {
|
|
|
|
|
return newPatternSyntaxError("Duplicate match name: %q.", bit)
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-06-21 19:49:06 +00:00
|
|
|
|
target.names = append(target.names, bit)
|
|
|
|
|
target.namesSet[bit] = struct{}{}
|
|
|
|
|
target.bits = append(target.bits, wildcardBit)
|
2016-02-25 22:59:38 +00:00
|
|
|
|
s = s[1+index:]
|
|
|
|
|
if "" == s {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-06-21 19:49:06 +00:00
|
|
|
|
return nil
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MustCompile is like Compile except that it never returns an error; but
|
|
|
|
|
// instead panic()s if there was an error.
|
|
|
|
|
//
|
|
|
|
|
// Example Usage:
|
|
|
|
|
//
|
|
|
|
|
// pattern := pathmath.MustCompile("/users/{user_id}")
|
|
|
|
|
//
|
|
|
|
|
// Note that if one recover()s from the panic(), one can use a Go type-switch
|
|
|
|
|
// to figure out what kind of error it is.
|
2019-06-21 06:28:50 +00:00
|
|
|
|
func MustCompile(uncompiledPattern string) *Pattern {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
var pattern Pattern
|
|
|
|
|
|
2019-06-21 20:16:25 +00:00
|
|
|
|
if err := CompileTo(&pattern, uncompiledPattern); nil != err {
|
2016-02-25 22:59:38 +00:00
|
|
|
|
panic(err)
|
|
|
|
|
} else {
|
2019-06-21 19:49:06 +00:00
|
|
|
|
return &pattern
|
2016-02-25 22:59:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|