package router import ( "fmt" "net/http" "regexp" ) type Handler interface { ServeHTTP(rctx *Context) } type HandlerFunc func(rctx *Context) func (handlerFunc HandlerFunc) ServeHTTP(rctx *Context) { handlerFunc(rctx) } type Router struct { routeHandler *Selector } func NewRouter() *Router { return &Router{ routeHandler: NewSelector(), } } func (rout *Router) Selector() *Selector { return rout.routeHandler } func (rout *Router) AddRoute(method, path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute(method, path, handlerFunc) } func (rout *Router) Head(path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute("HEAD", path, handlerFunc) } func (rout *Router) Get(path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute("GET", path, handlerFunc) } func (rout *Router) Post(path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute("POST", path, handlerFunc) } func (rout *Router) Put(path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute("PUT", path, handlerFunc) } func (rout *Router) Patch(path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute("PATCH", path, handlerFunc) } func (rout *Router) Delete(path string, handlerFunc HandlerFunc) { rout.routeHandler.AddRoute("DELETE", path, handlerFunc) } func (rout *Router) ServeHTTP(writer http.ResponseWriter, req *http.Request) { rctx := NewContext(writer, req) rout.routeHandler.ServeHTTP(rctx) } func (rout *Router) NotFound(handlerFunc HandlerFunc) { rout.routeHandler.notFound = handlerFunc } type Selector struct { Routes []*Route notFound Handler } func NewSelector() *Selector { notFound := HandlerFunc(func(ctx *Context) { http.NotFound(ctx.Writer, ctx.Request) }) return &Selector{ Routes: make([]*Route, 0), notFound: notFound, } } const globalRoutePattern = `^%s$` func (hand *Selector) AddRoute(method, path string, handlerFunc HandlerFunc) error { var err error rawPath := path path, err = pathCompiler(path) if err != nil { return err } path = fmt.Sprintf(globalRoutePattern, path) re, err := regexp.Compile(path) if err != nil { return err } route := &Route{ Method: method, Path: path, RawPath: rawPath, Handler: handlerFunc, Regexp: re, } hand.Routes = append(hand.Routes, route) return err } func (hand *Selector) ServeHTTP(rctx *Context) { realHandler := hand.notFound for _, route := range hand.Routes { match := route.Match(rctx.Request) if !match { continue } subvals := route.Regexp.FindStringSubmatch(rctx.Request.URL.Path) subkeys := route.Regexp.SubexpNames() for i, val := range subvals { key := subkeys[i] if key != "" { rctx.PathMap[key] = val } } realHandler = route.Handler break } realHandler.ServeHTTP(rctx) } type Route struct { Method string Regexp *regexp.Regexp Path string RawPath string Handler HandlerFunc } func (route Route) Match(req *http.Request) bool { if req.Method != route.Method { return false } match := route.Regexp.MatchString(req.URL.Path) if match { return true } return false }