@@ -65,6 +65,21 @@ type Options struct {
6565
6666 // how to encode uri
6767 Encode func (uri string , token interface {}) string
68+
69+ // how to decode uri
70+ Decode func (str string , token interface {}) string
71+ }
72+
73+ // MatchResult contains the result of match function
74+ type MatchResult struct {
75+ // matched url path
76+ Path string
77+
78+ // matched start index
79+ Index int
80+
81+ // matched params in url
82+ Params map [interface {}]interface {}
6883}
6984
7085// defaultDelimiter is the default delimiter of path.
@@ -174,13 +189,76 @@ func Compile(str string, o *Options) (func(interface{}, *Options) string, error)
174189// MustCompile is like Compile but panics if the expression cannot be compiled.
175190// It simplifies safe initialization of global variables.
176191func MustCompile (str string , o * Options ) func (interface {}, * Options ) string {
177- f , err := tokensToFunction ( Parse ( str , o ) , o )
192+ f , err := Compile ( str , o )
178193 if err != nil {
179194 panic (`pathtoregexp: Compile(` + quote (str ) + `): ` + err .Error ())
180195 }
181196 return f
182197}
183198
199+ // Match creates path match function from `path-to-regexp` spec.
200+ func Match (path interface {}, o * Options ) (func (string , * Options ) * MatchResult , error ) {
201+ var tokens []Token
202+ re , err := PathToRegexp (path , & tokens , o )
203+ if err != nil {
204+ return nil , err
205+ }
206+
207+ return regexpToFunction (re , tokens ), nil
208+ }
209+
210+ // MustMatch is like Match but panics if err occur in match function.
211+ func MustMatch (path interface {}, o * Options ) func (string , * Options ) * MatchResult {
212+ f , err := Match (path , o )
213+ if err != nil {
214+ panic (err )
215+ }
216+ return f
217+ }
218+
219+ // Create a path match function from `path-to-regexp` output.
220+ func regexpToFunction (re * regexp2.Regexp , tokens []Token ) func (string , * Options ) * MatchResult {
221+ return func (pathname string , options * Options ) * MatchResult {
222+ m , err := re .FindStringMatch (pathname )
223+ if m == nil || m .GroupCount () == 0 || err != nil {
224+ return nil
225+ }
226+
227+ path := m .Groups ()[0 ].String ()
228+ index := m .Index
229+ params := make (map [interface {}]interface {})
230+ decode := DecodeURIComponent
231+ if options != nil && options .Decode != nil {
232+ decode = options .Decode
233+ }
234+
235+ for i := 1 ; i < m .GroupCount (); i ++ {
236+ group := m .Groups ()[i ]
237+ if len (group .Captures ) == 0 {
238+ continue
239+ }
240+
241+ token := tokens [i - 1 ]
242+ matchedStr := group .String ()
243+
244+ if token .Repeat {
245+ arr := strings .Split (matchedStr , token .Delimiter )
246+ length := len (arr )
247+ if length > 0 {
248+ for i , str := range arr {
249+ arr [i ] = decode (str , token )
250+ }
251+ params [token .Name ] = arr
252+ }
253+ } else {
254+ params [token .Name ] = decode (matchedStr , token )
255+ }
256+ }
257+
258+ return & MatchResult {Path : path , Index : index , Params : params }
259+ }
260+ }
261+
184262// Expose a method for transforming tokens into the path function.
185263func tokensToFunction (tokens []interface {}, o * Options ) (
186264 func (interface {}, * Options ) string , error ) {
@@ -200,7 +278,7 @@ func tokensToFunction(tokens []interface{}, o *Options) (
200278
201279 return func (data interface {}, o * Options ) string {
202280 t := true
203- path , validate , encode := "" , & t , encodeURIComponent
281+ path , validate , encode := "" , & t , EncodeURIComponent
204282 if o != nil {
205283 if o .Encode != nil {
206284 encode = o .Encode
@@ -355,12 +433,20 @@ func toMap(data interface{}) map[interface{}]interface{} {
355433 return m
356434}
357435
358- func encodeURIComponent (str string , token interface {}) string {
436+ func EncodeURIComponent (str string , token interface {}) string {
359437 r := url .QueryEscape (str )
360438 r = strings .Replace (r , "+" , "%20" , - 1 )
361439 return r
362440}
363441
442+ func DecodeURIComponent (str string , token interface {}) string {
443+ r , err := url .QueryUnescape (str )
444+ if err != nil {
445+ panic (err )
446+ }
447+ return r
448+ }
449+
364450// Escape a regular expression string.
365451func escapeString (str string ) string {
366452 str , err := escapeRegexp .Replace (str , "\\ $1" , - 1 , - 1 )
@@ -408,11 +494,16 @@ func Must(r *regexp2.Regexp, err error) *regexp2.Regexp {
408494// Pull out tokens from a regexp.
409495func regexpToRegexp (path * regexp2.Regexp , tokens * []Token ) * regexp2.Regexp {
410496 if tokens != nil {
411- m , _ := tokenRegexp .FindStringMatch (path .String ())
412- if m != nil && m .GroupCount () > 0 {
413- newTokens := make ([]Token , 0 , len (* tokens )+ m .GroupCount ())
414- newTokens = append (newTokens , * tokens ... )
415- for i := 0 ; i < m .GroupCount (); i ++ {
497+ totalGroupCount := 0
498+ for m , _ := tokenRegexp .FindStringMatch (path .String ()); m != nil ; m ,
499+ _ = tokenRegexp .FindNextMatch (m ) {
500+ totalGroupCount += m .GroupCount ()
501+ }
502+
503+ if totalGroupCount > 0 {
504+ newTokens := append (make ([]Token , 0 ), * tokens ... )
505+
506+ for i := 0 ; i < totalGroupCount ; i ++ {
416507 newTokens = append (newTokens , Token {
417508 Name : i ,
418509 Prefix : "" ,
@@ -422,6 +513,7 @@ func regexpToRegexp(path *regexp2.Regexp, tokens *[]Token) *regexp2.Regexp {
422513 Pattern : "" ,
423514 })
424515 }
516+
425517 hdr := (* reflect .SliceHeader )(unsafe .Pointer (tokens ))
426518 * hdr = * (* reflect .SliceHeader )(unsafe .Pointer (& newTokens ))
427519 }
0 commit comments