You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
1.9 KiB
Go
112 lines
1.9 KiB
Go
2 years ago
|
package path
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/alecthomas/participle/v2"
|
||
|
"github.com/alecthomas/participle/v2/lexer"
|
||
|
)
|
||
|
|
||
|
type PathPart interface {
|
||
|
regex() string
|
||
|
}
|
||
|
|
||
|
type PathSlash struct {
|
||
|
Value string `"/"`
|
||
|
}
|
||
|
|
||
|
func (p PathSlash) regex() string {
|
||
|
return "/"
|
||
|
}
|
||
|
|
||
|
type PathShortPattern struct {
|
||
|
Name string `"{" @Ident "}"`
|
||
|
}
|
||
|
|
||
|
func (p PathShortPattern) regex() string {
|
||
|
return fmt.Sprintf("(?P<%s>[^/]+?)", p.Name)
|
||
|
}
|
||
|
|
||
|
type PathLongPattern struct {
|
||
|
Name string `"{{" @Ident "}}"`
|
||
|
}
|
||
|
|
||
|
func (p PathLongPattern) regex() string {
|
||
|
return fmt.Sprintf("(?P<%s>.+?)", p.Name)
|
||
|
}
|
||
|
|
||
|
type PathString struct {
|
||
|
Value string `@Ident`
|
||
|
}
|
||
|
|
||
|
func (p PathString) regex() string {
|
||
|
return regexp.QuoteMeta(p.Value)
|
||
|
}
|
||
|
|
||
|
type PathPattern struct {
|
||
|
Parts []PathPart `@@+`
|
||
|
}
|
||
|
|
||
|
func (pp PathPattern) regex() string {
|
||
|
sb := &strings.Builder{}
|
||
|
|
||
|
for _, p := range pp.Parts {
|
||
|
sb.WriteString(p.regex())
|
||
|
}
|
||
|
|
||
|
return sb.String()
|
||
|
}
|
||
|
|
||
|
func (pp PathPattern) Match(s string) (bool, map[string]string, error) {
|
||
|
r, err := regexp.Compile("^" + pp.regex() + "$")
|
||
|
if err != nil {
|
||
|
return false, nil, err
|
||
|
}
|
||
|
|
||
|
ms := r.FindStringSubmatch(s)
|
||
|
if ms == nil {
|
||
|
return false, nil, nil
|
||
|
}
|
||
|
|
||
|
ctx := map[string]string{}
|
||
|
for i, name := range r.SubexpNames() {
|
||
|
ctx[name] = ms[i]
|
||
|
}
|
||
|
|
||
|
return true, ctx, nil
|
||
|
}
|
||
|
|
||
|
var parser = participle.MustBuild[PathPattern](
|
||
|
participle.Union[PathPart](
|
||
|
&PathSlash{},
|
||
|
&PathLongPattern{},
|
||
|
&PathShortPattern{},
|
||
|
&PathString{},
|
||
|
),
|
||
|
participle.Lexer(
|
||
|
lexer.MustSimple([]lexer.SimpleRule{
|
||
|
{Name: "Slash", Pattern: `/`},
|
||
|
{Name: "PatternLongOpen", Pattern: `{{`},
|
||
|
{Name: "PatternLongClose", Pattern: `}}`},
|
||
|
{Name: "PatternShortOpen", Pattern: `{`},
|
||
|
{Name: "PatternShortClose", Pattern: `}`},
|
||
|
{Name: "Ident", Pattern: `[^/{}]+`},
|
||
|
}),
|
||
|
),
|
||
|
)
|
||
|
|
||
|
func ParsePattern(path string) (*PathPattern, error) {
|
||
|
return parser.ParseString("", path)
|
||
|
}
|
||
|
|
||
|
func MustParsePattern(path string) *PathPattern {
|
||
|
p, err := ParsePattern(path)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
return p
|
||
|
}
|