package mux import ( "fmt" "net/http" "strings" ) type Resource struct { mux *http.ServeMux pattern string middlewares []func(http.Handler) http.Handler routes *RouteList } // Resource routes mapping by using HTTP verbs // - GET /pattern view all resources // - GET /pattern/create new resource view // - POST /pattern create a new resource // - GET /pattern/:id view a resource // - PUT /pattern/:id update a resource // - PATCH /pattern/:id partial update a resource // - DELETE /resource/:id delete a resource func (m *Mux) Resource(pattern string, fn func(res *Resource), mw ...func(http.Handler) http.Handler) { if m == nil { panic("mux: Resource() called on nil") } if strings.TrimSpace(pattern) == "" { panic("mux: Resource() requires a patter to work") } if fn == nil { panic("mux: Resource() requires callback") } // Copy root middlewares. mws := make([]func(http.Handler) http.Handler, len(m.middlewares)+len(mw)) copy(mws, m.middlewares) // Append inline middlewares. if len(mw) > 0 { mws = append(mws, mw...) } fn(&Resource{ mux: m.mux, pattern: pattern, middlewares: mws, routes: m.routes, }) } // Index of all resource. // // GET /pattern func (res *Resource) Index(h http.HandlerFunc) { res.routes.Add(http.MethodGet + " " + res.pattern) res.handlerFunc(http.MethodGet, res.pattern, h) } // CreateView new resource // // GET /pattern/create func (res *Resource) CreateView(h http.HandlerFunc) { p := suffixIt(res.pattern, "create") res.routes.Add(http.MethodGet + " " + p) res.handlerFunc(http.MethodGet, p, h) } // Create a new resource // // POST /pattern/create func (res *Resource) Create(h http.HandlerFunc) { res.routes.Add(http.MethodPost + " " + res.pattern) res.handlerFunc(http.MethodPost, res.pattern, h) } // View a resource // // GET /pattern/:id func (res *Resource) View(h http.HandlerFunc) { p := suffixIt(res.pattern, "{id}") res.routes.Add(http.MethodGet + " " + p) res.handlerFunc(http.MethodGet, p, h) } // Update a resource // // PUT /pattern/:id func (res *Resource) Update(h http.HandlerFunc) { p := suffixIt(res.pattern, "{id}") res.routes.Add(http.MethodPut + " " + p) res.handlerFunc(http.MethodPut, p, h) } // UpdatePartial resource info // PATCH /pattern/:id func (res *Resource) UpdatePartial(h http.HandlerFunc) { p := suffixIt(res.pattern, "{id}") res.routes.Add(http.MethodPatch + " " + p) res.handlerFunc(http.MethodPatch, p, h) } // Delete a resource // // DELETE /pattern/:id func (res *Resource) Delete(h http.HandlerFunc) { p := suffixIt(res.pattern, "{id}") res.routes.Add(http.MethodDelete + " " + p) res.handlerFunc(http.MethodDelete, p, h) } func (res *Resource) Handle(pattern string, h http.HandlerFunc) { p := suffixIt(res.pattern, "{id}") res.routes.Add(http.MethodDelete + " " + p) res.handlerFunc(http.MethodDelete, p, h) } // handlerFunc registers the handler function for the given pattern. // If the given pattern conflicts, with one that is already registered, HandleFunc // panics. func (res *Resource) handlerFunc(method, pattern string, h http.HandlerFunc) { if res == nil { panic("serve: func handlerFunc() was called on nil") } if res.mux == nil { panic("serve: router mux is nil") } path := fmt.Sprintf("%s %s", method, pattern) res.mux.Handle(path, stack(res.middlewares, h)) } // Use will register middleware(s) on Router stack. func (res *Resource) Use(middlewares ...func(http.Handler) http.Handler) { if res == nil { panic("serve: func Use was called on nil") } res.middlewares = append(res.middlewares, middlewares...) } func suffixIt(str, suffix string) string { var p strings.Builder p.WriteString(str) if !strings.HasSuffix(str, "/") { p.WriteString("/") } p.WriteString(suffix) return p.String() }