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 }