blob: e8f11df221f089306926ac23775906a4a50cde62 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// Copyright 2012 The Gorilla Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package mux
6
7import (
8 "errors"
9 "fmt"
10 "net/http"
11 "net/url"
12 "regexp"
13 "strings"
14)
15
16// Route stores information to match a request and build URLs.
17type Route struct {
18 // Request handler for the route.
19 handler http.Handler
20 // If true, this route never matches: it is only used to build URLs.
21 buildOnly bool
22 // The name used to build URLs.
23 name string
24 // Error resulted from building a route.
25 err error
26
27 // "global" reference to all named routes
28 namedRoutes map[string]*Route
29
30 // config possibly passed in from `Router`
31 routeConf
32}
33
34// SkipClean reports whether path cleaning is enabled for this route via
35// Router.SkipClean.
36func (r *Route) SkipClean() bool {
37 return r.skipClean
38}
39
40// Match matches the route against the request.
41func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
42 if r.buildOnly || r.err != nil {
43 return false
44 }
45
46 var matchErr error
47
48 // Match everything.
49 for _, m := range r.matchers {
50 if matched := m.Match(req, match); !matched {
51 if _, ok := m.(methodMatcher); ok {
52 matchErr = ErrMethodMismatch
53 continue
54 }
55
56 // Ignore ErrNotFound errors. These errors arise from match call
57 // to Subrouters.
58 //
59 // This prevents subsequent matching subrouters from failing to
60 // run middleware. If not ignored, the middleware would see a
61 // non-nil MatchErr and be skipped, even when there was a
62 // matching route.
63 if match.MatchErr == ErrNotFound {
64 match.MatchErr = nil
65 }
66
vinodd213b092024-03-28 16:10:07 +053067 matchErr = nil // nolint:ineffassign
Naveen Sampath04696f72022-06-13 15:19:14 +053068 return false
vinodd213b092024-03-28 16:10:07 +053069 } else {
70 // Multiple routes may share the same path but use different HTTP methods. For instance:
71 // Route 1: POST "/users/{id}".
72 // Route 2: GET "/users/{id}", parameters: "id": "[0-9]+".
73 //
74 // The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2",
75 // The router should return a "Not Found" error as no route fully matches this request.
76 if match.MatchErr == ErrMethodMismatch {
77 match.MatchErr = nil
78 }
Naveen Sampath04696f72022-06-13 15:19:14 +053079 }
80 }
81
82 if matchErr != nil {
83 match.MatchErr = matchErr
84 return false
85 }
86
87 if match.MatchErr == ErrMethodMismatch && r.handler != nil {
88 // We found a route which matches request method, clear MatchErr
89 match.MatchErr = nil
90 // Then override the mis-matched handler
91 match.Handler = r.handler
92 }
93
94 // Yay, we have a match. Let's collect some info about it.
95 if match.Route == nil {
96 match.Route = r
97 }
98 if match.Handler == nil {
99 match.Handler = r.handler
100 }
101 if match.Vars == nil {
102 match.Vars = make(map[string]string)
103 }
104
105 // Set variables.
106 r.regexp.setMatch(req, match, r)
107 return true
108}
109
110// ----------------------------------------------------------------------------
111// Route attributes
112// ----------------------------------------------------------------------------
113
114// GetError returns an error resulted from building the route, if any.
115func (r *Route) GetError() error {
116 return r.err
117}
118
119// BuildOnly sets the route to never match: it is only used to build URLs.
120func (r *Route) BuildOnly() *Route {
121 r.buildOnly = true
122 return r
123}
124
125// Handler --------------------------------------------------------------------
126
127// Handler sets a handler for the route.
128func (r *Route) Handler(handler http.Handler) *Route {
129 if r.err == nil {
130 r.handler = handler
131 }
132 return r
133}
134
135// HandlerFunc sets a handler function for the route.
136func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
137 return r.Handler(http.HandlerFunc(f))
138}
139
140// GetHandler returns the handler for the route, if any.
141func (r *Route) GetHandler() http.Handler {
142 return r.handler
143}
144
145// Name -----------------------------------------------------------------------
146
147// Name sets the name for the route, used to build URLs.
148// It is an error to call Name more than once on a route.
149func (r *Route) Name(name string) *Route {
150 if r.name != "" {
151 r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
152 r.name, name)
153 }
154 if r.err == nil {
155 r.name = name
156 r.namedRoutes[name] = r
157 }
158 return r
159}
160
161// GetName returns the name for the route, if any.
162func (r *Route) GetName() string {
163 return r.name
164}
165
166// ----------------------------------------------------------------------------
167// Matchers
168// ----------------------------------------------------------------------------
169
170// matcher types try to match a request.
171type matcher interface {
172 Match(*http.Request, *RouteMatch) bool
173}
174
175// addMatcher adds a matcher to the route.
176func (r *Route) addMatcher(m matcher) *Route {
177 if r.err == nil {
178 r.matchers = append(r.matchers, m)
179 }
180 return r
181}
182
183// addRegexpMatcher adds a host or path matcher and builder to a route.
184func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
185 if r.err != nil {
186 return r.err
187 }
188 if typ == regexpTypePath || typ == regexpTypePrefix {
189 if len(tpl) > 0 && tpl[0] != '/' {
190 return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
191 }
192 if r.regexp.path != nil {
193 tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
194 }
195 }
196 rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
197 strictSlash: r.strictSlash,
198 useEncodedPath: r.useEncodedPath,
199 })
200 if err != nil {
201 return err
202 }
203 for _, q := range r.regexp.queries {
204 if err = uniqueVars(rr.varsN, q.varsN); err != nil {
205 return err
206 }
207 }
208 if typ == regexpTypeHost {
209 if r.regexp.path != nil {
210 if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
211 return err
212 }
213 }
214 r.regexp.host = rr
215 } else {
216 if r.regexp.host != nil {
217 if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
218 return err
219 }
220 }
221 if typ == regexpTypeQuery {
222 r.regexp.queries = append(r.regexp.queries, rr)
223 } else {
224 r.regexp.path = rr
225 }
226 }
227 r.addMatcher(rr)
228 return nil
229}
230
231// Headers --------------------------------------------------------------------
232
233// headerMatcher matches the request against header values.
234type headerMatcher map[string]string
235
236func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
237 return matchMapWithString(m, r.Header, true)
238}
239
240// Headers adds a matcher for request header values.
241// It accepts a sequence of key/value pairs to be matched. For example:
242//
vinodd213b092024-03-28 16:10:07 +0530243// r := mux.NewRouter().NewRoute()
244// r.Headers("Content-Type", "application/json",
245// "X-Requested-With", "XMLHttpRequest")
Naveen Sampath04696f72022-06-13 15:19:14 +0530246//
247// The above route will only match if both request header values match.
248// If the value is an empty string, it will match any value if the key is set.
249func (r *Route) Headers(pairs ...string) *Route {
250 if r.err == nil {
251 var headers map[string]string
252 headers, r.err = mapFromPairsToString(pairs...)
253 return r.addMatcher(headerMatcher(headers))
254 }
255 return r
256}
257
258// headerRegexMatcher matches the request against the route given a regex for the header
259type headerRegexMatcher map[string]*regexp.Regexp
260
261func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
262 return matchMapWithRegex(m, r.Header, true)
263}
264
265// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
266// support. For example:
267//
vinodd213b092024-03-28 16:10:07 +0530268// r := mux.NewRouter().NewRoute()
269// r.HeadersRegexp("Content-Type", "application/(text|json)",
270// "X-Requested-With", "XMLHttpRequest")
Naveen Sampath04696f72022-06-13 15:19:14 +0530271//
272// The above route will only match if both the request header matches both regular expressions.
273// If the value is an empty string, it will match any value if the key is set.
274// Use the start and end of string anchors (^ and $) to match an exact value.
275func (r *Route) HeadersRegexp(pairs ...string) *Route {
276 if r.err == nil {
277 var headers map[string]*regexp.Regexp
278 headers, r.err = mapFromPairsToRegex(pairs...)
279 return r.addMatcher(headerRegexMatcher(headers))
280 }
281 return r
282}
283
284// Host -----------------------------------------------------------------------
285
286// Host adds a matcher for the URL host.
287// It accepts a template with zero or more URL variables enclosed by {}.
288// Variables can define an optional regexp pattern to be matched:
289//
290// - {name} matches anything until the next dot.
291//
292// - {name:pattern} matches the given regexp pattern.
293//
294// For example:
295//
vinodd213b092024-03-28 16:10:07 +0530296// r := mux.NewRouter().NewRoute()
297// r.Host("www.example.com")
298// r.Host("{subdomain}.domain.com")
299// r.Host("{subdomain:[a-z]+}.domain.com")
Naveen Sampath04696f72022-06-13 15:19:14 +0530300//
301// Variable names must be unique in a given route. They can be retrieved
302// calling mux.Vars(request).
303func (r *Route) Host(tpl string) *Route {
304 r.err = r.addRegexpMatcher(tpl, regexpTypeHost)
305 return r
306}
307
308// MatcherFunc ----------------------------------------------------------------
309
310// MatcherFunc is the function signature used by custom matchers.
311type MatcherFunc func(*http.Request, *RouteMatch) bool
312
313// Match returns the match for a given request.
314func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
315 return m(r, match)
316}
317
318// MatcherFunc adds a custom function to be used as request matcher.
319func (r *Route) MatcherFunc(f MatcherFunc) *Route {
320 return r.addMatcher(f)
321}
322
323// Methods --------------------------------------------------------------------
324
325// methodMatcher matches the request against HTTP methods.
326type methodMatcher []string
327
328func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
329 return matchInArray(m, r.Method)
330}
331
332// Methods adds a matcher for HTTP methods.
333// It accepts a sequence of one or more methods to be matched, e.g.:
334// "GET", "POST", "PUT".
335func (r *Route) Methods(methods ...string) *Route {
336 for k, v := range methods {
337 methods[k] = strings.ToUpper(v)
338 }
339 return r.addMatcher(methodMatcher(methods))
340}
341
342// Path -----------------------------------------------------------------------
343
344// Path adds a matcher for the URL path.
345// It accepts a template with zero or more URL variables enclosed by {}. The
346// template must start with a "/".
347// Variables can define an optional regexp pattern to be matched:
348//
349// - {name} matches anything until the next slash.
350//
351// - {name:pattern} matches the given regexp pattern.
352//
353// For example:
354//
vinodd213b092024-03-28 16:10:07 +0530355// r := mux.NewRouter().NewRoute()
356// r.Path("/products/").Handler(ProductsHandler)
357// r.Path("/products/{key}").Handler(ProductsHandler)
358// r.Path("/articles/{category}/{id:[0-9]+}").
359// Handler(ArticleHandler)
Naveen Sampath04696f72022-06-13 15:19:14 +0530360//
361// Variable names must be unique in a given route. They can be retrieved
362// calling mux.Vars(request).
363func (r *Route) Path(tpl string) *Route {
364 r.err = r.addRegexpMatcher(tpl, regexpTypePath)
365 return r
366}
367
368// PathPrefix -----------------------------------------------------------------
369
370// PathPrefix adds a matcher for the URL path prefix. This matches if the given
371// template is a prefix of the full URL path. See Route.Path() for details on
372// the tpl argument.
373//
374// Note that it does not treat slashes specially ("/foobar/" will be matched by
375// the prefix "/foo") so you may want to use a trailing slash here.
376//
377// Also note that the setting of Router.StrictSlash() has no effect on routes
378// with a PathPrefix matcher.
379func (r *Route) PathPrefix(tpl string) *Route {
380 r.err = r.addRegexpMatcher(tpl, regexpTypePrefix)
381 return r
382}
383
384// Query ----------------------------------------------------------------------
385
386// Queries adds a matcher for URL query values.
387// It accepts a sequence of key/value pairs. Values may define variables.
388// For example:
389//
vinodd213b092024-03-28 16:10:07 +0530390// r := mux.NewRouter().NewRoute()
391// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
Naveen Sampath04696f72022-06-13 15:19:14 +0530392//
393// The above route will only match if the URL contains the defined queries
394// values, e.g.: ?foo=bar&id=42.
395//
396// If the value is an empty string, it will match any value if the key is set.
397//
398// Variables can define an optional regexp pattern to be matched:
399//
400// - {name} matches anything until the next slash.
401//
402// - {name:pattern} matches the given regexp pattern.
403func (r *Route) Queries(pairs ...string) *Route {
404 length := len(pairs)
405 if length%2 != 0 {
406 r.err = fmt.Errorf(
407 "mux: number of parameters must be multiple of 2, got %v", pairs)
408 return nil
409 }
410 for i := 0; i < length; i += 2 {
411 if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil {
412 return r
413 }
414 }
415
416 return r
417}
418
419// Schemes --------------------------------------------------------------------
420
421// schemeMatcher matches the request against URL schemes.
422type schemeMatcher []string
423
424func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
425 scheme := r.URL.Scheme
426 // https://golang.org/pkg/net/http/#Request
427 // "For [most] server requests, fields other than Path and RawQuery will be
428 // empty."
429 // Since we're an http muxer, the scheme is either going to be http or https
430 // though, so we can just set it based on the tls termination state.
431 if scheme == "" {
432 if r.TLS == nil {
433 scheme = "http"
434 } else {
435 scheme = "https"
436 }
437 }
438 return matchInArray(m, scheme)
439}
440
441// Schemes adds a matcher for URL schemes.
442// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
443// If the request's URL has a scheme set, it will be matched against.
444// Generally, the URL scheme will only be set if a previous handler set it,
445// such as the ProxyHeaders handler from gorilla/handlers.
446// If unset, the scheme will be determined based on the request's TLS
447// termination state.
448// The first argument to Schemes will be used when constructing a route URL.
449func (r *Route) Schemes(schemes ...string) *Route {
450 for k, v := range schemes {
451 schemes[k] = strings.ToLower(v)
452 }
453 if len(schemes) > 0 {
454 r.buildScheme = schemes[0]
455 }
456 return r.addMatcher(schemeMatcher(schemes))
457}
458
459// BuildVarsFunc --------------------------------------------------------------
460
461// BuildVarsFunc is the function signature used by custom build variable
462// functions (which can modify route variables before a route's URL is built).
463type BuildVarsFunc func(map[string]string) map[string]string
464
465// BuildVarsFunc adds a custom function to be used to modify build variables
466// before a route's URL is built.
467func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
468 if r.buildVarsFunc != nil {
469 // compose the old and new functions
470 old := r.buildVarsFunc
471 r.buildVarsFunc = func(m map[string]string) map[string]string {
472 return f(old(m))
473 }
474 } else {
475 r.buildVarsFunc = f
476 }
477 return r
478}
479
480// Subrouter ------------------------------------------------------------------
481
482// Subrouter creates a subrouter for the route.
483//
484// It will test the inner routes only if the parent route matched. For example:
485//
vinodd213b092024-03-28 16:10:07 +0530486// r := mux.NewRouter().NewRoute()
487// s := r.Host("www.example.com").Subrouter()
488// s.HandleFunc("/products/", ProductsHandler)
489// s.HandleFunc("/products/{key}", ProductHandler)
490// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
Naveen Sampath04696f72022-06-13 15:19:14 +0530491//
492// Here, the routes registered in the subrouter won't be tested if the host
493// doesn't match.
494func (r *Route) Subrouter() *Router {
495 // initialize a subrouter with a copy of the parent route's configuration
496 router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
497 r.addMatcher(router)
498 return router
499}
500
501// ----------------------------------------------------------------------------
502// URL building
503// ----------------------------------------------------------------------------
504
505// URL builds a URL for the route.
506//
507// It accepts a sequence of key/value pairs for the route variables. For
508// example, given this route:
509//
vinodd213b092024-03-28 16:10:07 +0530510// r := mux.NewRouter()
511// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
512// Name("article")
Naveen Sampath04696f72022-06-13 15:19:14 +0530513//
514// ...a URL for it can be built using:
515//
vinodd213b092024-03-28 16:10:07 +0530516// url, err := r.Get("article").URL("category", "technology", "id", "42")
Naveen Sampath04696f72022-06-13 15:19:14 +0530517//
518// ...which will return an url.URL with the following path:
519//
vinodd213b092024-03-28 16:10:07 +0530520// "/articles/technology/42"
Naveen Sampath04696f72022-06-13 15:19:14 +0530521//
522// This also works for host variables:
523//
vinodd213b092024-03-28 16:10:07 +0530524// r := mux.NewRouter()
525// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
526// Host("{subdomain}.domain.com").
527// Name("article")
Naveen Sampath04696f72022-06-13 15:19:14 +0530528//
vinodd213b092024-03-28 16:10:07 +0530529// // url.String() will be "http://news.domain.com/articles/technology/42"
530// url, err := r.Get("article").URL("subdomain", "news",
531// "category", "technology",
532// "id", "42")
Naveen Sampath04696f72022-06-13 15:19:14 +0530533//
534// The scheme of the resulting url will be the first argument that was passed to Schemes:
535//
vinodd213b092024-03-28 16:10:07 +0530536// // url.String() will be "https://example.com"
537// r := mux.NewRouter().NewRoute()
538// url, err := r.Host("example.com")
539// .Schemes("https", "http").URL()
Naveen Sampath04696f72022-06-13 15:19:14 +0530540//
541// All variables defined in the route are required, and their values must
542// conform to the corresponding patterns.
543func (r *Route) URL(pairs ...string) (*url.URL, error) {
544 if r.err != nil {
545 return nil, r.err
546 }
547 values, err := r.prepareVars(pairs...)
548 if err != nil {
549 return nil, err
550 }
551 var scheme, host, path string
552 queries := make([]string, 0, len(r.regexp.queries))
553 if r.regexp.host != nil {
554 if host, err = r.regexp.host.url(values); err != nil {
555 return nil, err
556 }
557 scheme = "http"
558 if r.buildScheme != "" {
559 scheme = r.buildScheme
560 }
561 }
562 if r.regexp.path != nil {
563 if path, err = r.regexp.path.url(values); err != nil {
564 return nil, err
565 }
566 }
567 for _, q := range r.regexp.queries {
568 var query string
569 if query, err = q.url(values); err != nil {
570 return nil, err
571 }
572 queries = append(queries, query)
573 }
574 return &url.URL{
575 Scheme: scheme,
576 Host: host,
577 Path: path,
578 RawQuery: strings.Join(queries, "&"),
579 }, nil
580}
581
582// URLHost builds the host part of the URL for a route. See Route.URL().
583//
584// The route must have a host defined.
585func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
586 if r.err != nil {
587 return nil, r.err
588 }
589 if r.regexp.host == nil {
590 return nil, errors.New("mux: route doesn't have a host")
591 }
592 values, err := r.prepareVars(pairs...)
593 if err != nil {
594 return nil, err
595 }
596 host, err := r.regexp.host.url(values)
597 if err != nil {
598 return nil, err
599 }
600 u := &url.URL{
601 Scheme: "http",
602 Host: host,
603 }
604 if r.buildScheme != "" {
605 u.Scheme = r.buildScheme
606 }
607 return u, nil
608}
609
610// URLPath builds the path part of the URL for a route. See Route.URL().
611//
612// The route must have a path defined.
613func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
614 if r.err != nil {
615 return nil, r.err
616 }
617 if r.regexp.path == nil {
618 return nil, errors.New("mux: route doesn't have a path")
619 }
620 values, err := r.prepareVars(pairs...)
621 if err != nil {
622 return nil, err
623 }
624 path, err := r.regexp.path.url(values)
625 if err != nil {
626 return nil, err
627 }
628 return &url.URL{
629 Path: path,
630 }, nil
631}
632
633// GetPathTemplate returns the template used to build the
634// route match.
635// This is useful for building simple REST API documentation and for instrumentation
636// against third-party services.
637// An error will be returned if the route does not define a path.
638func (r *Route) GetPathTemplate() (string, error) {
639 if r.err != nil {
640 return "", r.err
641 }
642 if r.regexp.path == nil {
643 return "", errors.New("mux: route doesn't have a path")
644 }
645 return r.regexp.path.template, nil
646}
647
648// GetPathRegexp returns the expanded regular expression used to match route path.
649// This is useful for building simple REST API documentation and for instrumentation
650// against third-party services.
651// An error will be returned if the route does not define a path.
652func (r *Route) GetPathRegexp() (string, error) {
653 if r.err != nil {
654 return "", r.err
655 }
656 if r.regexp.path == nil {
657 return "", errors.New("mux: route does not have a path")
658 }
659 return r.regexp.path.regexp.String(), nil
660}
661
662// GetQueriesRegexp returns the expanded regular expressions used to match the
663// route queries.
664// This is useful for building simple REST API documentation and for instrumentation
665// against third-party services.
666// An error will be returned if the route does not have queries.
667func (r *Route) GetQueriesRegexp() ([]string, error) {
668 if r.err != nil {
669 return nil, r.err
670 }
671 if r.regexp.queries == nil {
672 return nil, errors.New("mux: route doesn't have queries")
673 }
674 queries := make([]string, 0, len(r.regexp.queries))
675 for _, query := range r.regexp.queries {
676 queries = append(queries, query.regexp.String())
677 }
678 return queries, nil
679}
680
681// GetQueriesTemplates returns the templates used to build the
682// query matching.
683// This is useful for building simple REST API documentation and for instrumentation
684// against third-party services.
685// An error will be returned if the route does not define queries.
686func (r *Route) GetQueriesTemplates() ([]string, error) {
687 if r.err != nil {
688 return nil, r.err
689 }
690 if r.regexp.queries == nil {
691 return nil, errors.New("mux: route doesn't have queries")
692 }
693 queries := make([]string, 0, len(r.regexp.queries))
694 for _, query := range r.regexp.queries {
695 queries = append(queries, query.template)
696 }
697 return queries, nil
698}
699
700// GetMethods returns the methods the route matches against
701// This is useful for building simple REST API documentation and for instrumentation
702// against third-party services.
703// An error will be returned if route does not have methods.
704func (r *Route) GetMethods() ([]string, error) {
705 if r.err != nil {
706 return nil, r.err
707 }
708 for _, m := range r.matchers {
709 if methods, ok := m.(methodMatcher); ok {
710 return []string(methods), nil
711 }
712 }
713 return nil, errors.New("mux: route doesn't have methods")
714}
715
716// GetHostTemplate returns the template used to build the
717// route match.
718// This is useful for building simple REST API documentation and for instrumentation
719// against third-party services.
720// An error will be returned if the route does not define a host.
721func (r *Route) GetHostTemplate() (string, error) {
722 if r.err != nil {
723 return "", r.err
724 }
725 if r.regexp.host == nil {
726 return "", errors.New("mux: route doesn't have a host")
727 }
728 return r.regexp.host.template, nil
729}
730
vinodd213b092024-03-28 16:10:07 +0530731// GetVarNames returns the names of all variables added by regexp matchers
732// These can be used to know which route variables should be passed into r.URL()
733func (r *Route) GetVarNames() ([]string, error) {
734 if r.err != nil {
735 return nil, r.err
736 }
737 var varNames []string
738 if r.regexp.host != nil {
739 varNames = append(varNames, r.regexp.host.varsN...)
740 }
741 if r.regexp.path != nil {
742 varNames = append(varNames, r.regexp.path.varsN...)
743 }
744 for _, regx := range r.regexp.queries {
745 varNames = append(varNames, regx.varsN...)
746 }
747 return varNames, nil
748}
749
Naveen Sampath04696f72022-06-13 15:19:14 +0530750// prepareVars converts the route variable pairs into a map. If the route has a
751// BuildVarsFunc, it is invoked.
752func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
753 m, err := mapFromPairsToString(pairs...)
754 if err != nil {
755 return nil, err
756 }
757 return r.buildVars(m), nil
758}
759
760func (r *Route) buildVars(m map[string]string) map[string]string {
761 if r.buildVarsFunc != nil {
762 m = r.buildVarsFunc(m)
763 }
764 return m
765}