@@ -65,6 +65,21 @@ type Options struct {
65
65
66
66
// how to encode uri
67
67
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 {}
68
83
}
69
84
70
85
// defaultDelimiter is the default delimiter of path.
@@ -174,13 +189,76 @@ func Compile(str string, o *Options) (func(interface{}, *Options) string, error)
174
189
// MustCompile is like Compile but panics if the expression cannot be compiled.
175
190
// It simplifies safe initialization of global variables.
176
191
func MustCompile (str string , o * Options ) func (interface {}, * Options ) string {
177
- f , err := tokensToFunction ( Parse ( str , o ) , o )
192
+ f , err := Compile ( str , o )
178
193
if err != nil {
179
194
panic (`pathtoregexp: Compile(` + quote (str ) + `): ` + err .Error ())
180
195
}
181
196
return f
182
197
}
183
198
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
+
184
262
// Expose a method for transforming tokens into the path function.
185
263
func tokensToFunction (tokens []interface {}, o * Options ) (
186
264
func (interface {}, * Options ) string , error ) {
@@ -200,7 +278,7 @@ func tokensToFunction(tokens []interface{}, o *Options) (
200
278
201
279
return func (data interface {}, o * Options ) string {
202
280
t := true
203
- path , validate , encode := "" , & t , encodeURIComponent
281
+ path , validate , encode := "" , & t , EncodeURIComponent
204
282
if o != nil {
205
283
if o .Encode != nil {
206
284
encode = o .Encode
@@ -355,12 +433,20 @@ func toMap(data interface{}) map[interface{}]interface{} {
355
433
return m
356
434
}
357
435
358
- func encodeURIComponent (str string , token interface {}) string {
436
+ func EncodeURIComponent (str string , token interface {}) string {
359
437
r := url .QueryEscape (str )
360
438
r = strings .Replace (r , "+" , "%20" , - 1 )
361
439
return r
362
440
}
363
441
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
+
364
450
// Escape a regular expression string.
365
451
func escapeString (str string ) string {
366
452
str , err := escapeRegexp .Replace (str , "\\ $1" , - 1 , - 1 )
@@ -408,11 +494,16 @@ func Must(r *regexp2.Regexp, err error) *regexp2.Regexp {
408
494
// Pull out tokens from a regexp.
409
495
func regexpToRegexp (path * regexp2.Regexp , tokens * []Token ) * regexp2.Regexp {
410
496
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 ++ {
416
507
newTokens = append (newTokens , Token {
417
508
Name : i ,
418
509
Prefix : "" ,
@@ -422,6 +513,7 @@ func regexpToRegexp(path *regexp2.Regexp, tokens *[]Token) *regexp2.Regexp {
422
513
Pattern : "" ,
423
514
})
424
515
}
516
+
425
517
hdr := (* reflect .SliceHeader )(unsafe .Pointer (tokens ))
426
518
* hdr = * (* reflect .SliceHeader )(unsafe .Pointer (& newTokens ))
427
519
}
0 commit comments