fupie/ws/route/router.go

130 lines
2.7 KiB
Go

package route
import (
"context"
"fmt"
"net/http"
"strings"
"sectorinf.com/emilis/fupie/logie"
)
type Method string
type Route struct {
Pattern PartPattern
Handler HandlerFunc
}
type Routes []Route
type Middleware func(http.Request, Keeper)
var (
NotFoundHandler = func(ctx Context) {
ctx.Status(http.StatusNotFound)
}
)
func (r Routes) Match(path string) HandlerFunc {
for _, route := range r {
if route.Pattern.Match(path) {
return route.Handler
}
}
return NotFoundHandler
}
type HandlerFunc func(Context)
type Router struct {
fnMatcher map[Method]Routes
middleware []Middleware
logger logie.Log
}
func New(logger logie.Log) Router {
return Router{
fnMatcher: map[Method]Routes{
http.MethodGet: {},
http.MethodConnect: {},
http.MethodPatch: {},
http.MethodPost: {},
http.MethodPut: {},
http.MethodDelete: {},
http.MethodHead: {},
http.MethodOptions: {},
http.MethodTrace: {},
},
logger: logger,
middleware: []Middleware{},
}
}
func (r Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
byMethod, exists := r.fnMatcher[Method(req.Method)]
if !exists {
w.WriteHeader(http.StatusNotFound)
return
}
fn := byMethod.Match(req.URL.Path)
rwk := &ResponseWriterKeeper{
ResponseWriter: w,
Keeper: Keeper{
// Default 200
Status: http.StatusOK,
},
}
fn(Context{
Request: req,
Writer: rwk,
Context: context.Background(),
Log: r.logger,
})
for _, mware := range r.middleware {
mware(*req, rwk.Keeper)
}
}
func (r Router) With(method, pathExpr string, handler HandlerFunc) Router {
r.fnMatcher[Method(method)] = append(r.fnMatcher[Method(method)], Route{
Pattern: Pattern(pathExpr),
Handler: handler,
})
return r
}
func (r Router) GET(pathExpr string, handler HandlerFunc) Router {
return r.With(http.MethodGet, pathExpr, handler)
}
func (r Router) POST(pathExpr string, handler HandlerFunc) Router {
return r.With(http.MethodPost, pathExpr, handler)
}
func (r Router) PATCH(pathExpr string, handler HandlerFunc) Router {
return r.With(http.MethodPatch, pathExpr, handler)
}
func (r Router) PUT(pathExpr string, handler HandlerFunc) Router {
return r.With(http.MethodPut, pathExpr, handler)
}
func (r Router) DELETE(pathExpr string, handler HandlerFunc) Router {
return r.With(http.MethodDelete, pathExpr, handler)
}
func (r Router) Middleware(mware ...Middleware) Router {
r.middleware = mware
return r
}
func (r Router) Group(prefix string, fn func(Router) Router) Router {
prefix = strings.TrimSuffix(prefix, "/")
group := fn(New(r.logger))
for method, byRoute := range group.fnMatcher {
for _, route := range byRoute {
r = r.With(string(method), fmt.Sprintf("%s/%s", prefix, route.Pattern.String()), route.Handler)
}
}
return r
}