123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // Copyright 2018 Keelan Lightfoot. All rights reserved.
- // Copyright 2013 Julien Schmidt. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be found
- // in the LICENSE file.
-
- // Package pathrouter is a trie based high performance generic request router.
- //
- // A trivial example is:
- //
- // package main
- // import (
- // "fmt"
- // "github.com/naleek/pathrouter"
- // )
- //
- // func Hello(ps pathrouter.Params, d interface{}) {
- // u := d.(string)
- // fmt.Printf("hello, %s!\nUser Data: %s\n", ps.ByName("name"), u)
- // }
- //
- // func main() {
- // router := pathrouter.New()
- // router.Handle("/hello/:name", Hello)
- //
- // router.Execute("/hello/bob", "boring user data")
- // }
- //
- // The router matches incoming requests by the path.
- // If a handle is registered for this path and method, the router delegates the
- // request to that function.
- //
- // The registered path, against which the router matches incoming requests, can
- // contain two types of parameters:
- // Syntax Type
- // :name named parameter
- // *name catch-all parameter
- //
- // Named parameters are dynamic path segments. They match anything until the
- // next '/' or the path end:
- // Path: /blog/:category/:post
- //
- // Requests:
- // /blog/go/request-routers match: category="go", post="request-routers"
- // /blog/go/request-routers/ no match, but the router would redirect
- // /blog/go/ no match
- // /blog/go/request-routers/comments no match
- //
- // Catch-all parameters match anything until the path end, including the
- // directory index (the '/' before the catch-all). Since they match anything
- // until the end, catch-all parameters must always be the final path element.
- // Path: /files/*filepath
- //
- // Requests:
- // /files/ match: filepath="/"
- // /files/LICENSE match: filepath="/LICENSE"
- // /files/templates/article.html match: filepath="/templates/article.html"
- // /files no match, but the router would redirect
- //
- // The value of parameters is saved as a slice of the Param struct, consisting
- // each of a key and a value. The slice is passed to the Handle func as a third
- // parameter.
- // There are two ways to retrieve the value of a parameter:
- // // by the name of the parameter
- // user := ps.ByName("user") // defined by :user or *user
- //
- // // by the index of the parameter. This way you can also get the name (key)
- // thirdKey := ps[2].Key // the name of the 3rd parameter
- // thirdValue := ps[2].Value // the value of the 3rd parameter
- package pathrouter
-
- import (
- "errors"
- )
-
- var (
- // ErrRouteNotFound is reported when a matching route is not found
- ErrRouteNotFound = errors.New("pathrouter: no matching route found")
- )
-
- // Handle is a function that can be registered to a route to handle HTTP
- // requests. Like http.HandlerFunc, but has a third parameter for the values of
- // wildcards (variables).
- type Handle func(Params, interface{})
-
- // Param is a single URL parameter, consisting of a key and a value.
- type Param struct {
- Key string
- Value string
- }
-
- // Params is a Param-slice, as returned by the router.
- // The slice is ordered, the first URL parameter is also the first slice value.
- // It is therefore safe to read values by the index.
- type Params []Param
-
- // ByName returns the value of the first Param which key matches the given name.
- // If no matching Param is found, an empty string is returned.
- func (ps Params) ByName(name string) string {
- for i := range ps {
- if ps[i].Key == name {
- return ps[i].Value
- }
- }
- return ""
- }
-
- // Router is a http.Handler which can be used to dispatch requests to different
- // handler functions via configurable routes
- type Router struct {
- root *node
- }
-
- // New returns a new initialized Router.
- func New() *Router {
- return &Router{}
- }
-
- // Handle registers a new request handle with the given path.
- func (r *Router) Handle(path string, handle Handle) error {
- if path[0] != '/' {
- panic("path must begin with '/'")
- }
-
- if r.root == nil {
- r.root = new(node)
- }
-
- r.root.addRoute(path, handle)
-
- return nil
- }
-
- // Lookup allows the manual lookup of a method + path combo.
- // This is e.g. useful to build a framework around this router.
- // If the path was found, it returns the handle function and the path parameter
- // values.
- func (r *Router) Lookup(method, path string) (Handle, Params) {
- if root := r.root; root != nil {
- return root.getValue(path)
- }
- return nil, nil
- }
-
- // Execute runs the route if it exists
- func (r *Router) Execute(path string, userdata interface{}) error {
- if r.root != nil {
- if handle, ps := r.root.getValue(path); handle != nil {
- handle(ps, userdata)
- return nil
- }
- }
- return ErrRouteNotFound
- }
|