Skip to content

Commit

Permalink
Implement Fluent Method Chaining for Status and Type Methods Using Ge…
Browse files Browse the repository at this point in the history
…nerics #3221
  • Loading branch information
ReneWerner87 committed Jan 28, 2025
1 parent 8970f51 commit aac1b97
Show file tree
Hide file tree
Showing 20 changed files with 650 additions and 535 deletions.
143 changes: 86 additions & 57 deletions app.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
type CustomBinder interface {
Name() string
MIMETypes() []string
Parse(c Ctx, out any) error
Parse(c Ctx[any], out any) error
}

// StructValidator is an interface to register custom struct validator for binding.
Expand All @@ -19,7 +19,7 @@ type StructValidator interface {

// Bind struct
type Bind struct {
ctx Ctx
ctx Ctx[any]
dontHandleErrs bool
}

Expand Down
21 changes: 11 additions & 10 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ const userContextKey contextKey = 0 // __local_user_context__
// generation tool `go install github.com/vburenin/ifacemaker@975a95966976eeb2d4365a7fb236e274c54da64c`
// https://github.com/vburenin/ifacemaker/blob/975a95966976eeb2d4365a7fb236e274c54da64c/ifacemaker.go#L14-L30
//
//go:generate ifacemaker --file ctx.go --struct DefaultCtx --iface Ctx --pkg fiber --output ctx_interface_gen.go --not-exported true --iface-comment "Ctx represents the Context which hold the HTTP request and response.\nIt has methods for the request query string, parameters, body, HTTP headers and so on."
//go:generate ifacemaker --file ctx.go --struct DefaultCtx --iface Ctx --pkg fiber --output ctx_interface.go --not-exported true --iface-comment "Ctx represents the Context which hold the HTTP request and response.\nIt has methods for the request query string, parameters, body, HTTP headers and so on."
//go:generate go run ctx_interface_gen.go
type DefaultCtx struct {
app *App // Reference to *App
app *App[Ctx[any]] // Reference to *App

Check failure on line 53 in ctx.go

View workflow job for this annotation

GitHub Actions / unit (1.23.x, ubuntu-latest)

Ctx[any] does not satisfy Ctx[Ctx[any]] (wrong type for method App)

Check failure on line 53 in ctx.go

View workflow job for this annotation

GitHub Actions / govulncheck-check

Ctx[any] does not satisfy Ctx[Ctx[any]] (wrong type for method App)

Check failure on line 53 in ctx.go

View workflow job for this annotation

GitHub Actions / lint

Ctx[any] does not satisfy Ctx[Ctx[any]] (wrong type for method App)

Check failure on line 53 in ctx.go

View workflow job for this annotation

GitHub Actions / repeated

Ctx[any] does not satisfy Ctx[Ctx[any]] (wrong type for method App)

Check failure on line 53 in ctx.go

View workflow job for this annotation

GitHub Actions / Compare

Ctx[any] does not satisfy Ctx[Ctx[any]] (wrong type for method App)
route *Route // Reference to *Route
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
bind *Bind // Default bind reference
Expand Down Expand Up @@ -197,7 +198,7 @@ type Views interface {

// ResFmt associates a Content Type to a fiber.Handler for c.Format
type ResFmt struct {
Handler func(Ctx) error
Handler func(Ctx[any]) error
MediaType string
}

Expand All @@ -222,7 +223,7 @@ func (c *DefaultCtx) AcceptsLanguages(offers ...string) string {
}

// App returns the *App reference to the instance of the Fiber application
func (c *DefaultCtx) App() *App {
func (c *DefaultCtx) App() *App[DefaultCtx] {
return c.app
}

Expand Down Expand Up @@ -642,7 +643,7 @@ func (c *DefaultCtx) Get(key string, defaultValue ...string) string {

// GetReqHeader returns the HTTP request header specified by filed.
// This function is generic and can handle different headers type values.
func GetReqHeader[V GenericType](c Ctx, key string, defaultValue ...V) V {
func GetReqHeader[V GenericType](c Ctx[any], key string, defaultValue ...V) V {
var v V
return genericParseType[V](c.App().getString(c.Request().Header.Peek(key)), v, defaultValue...)
}
Expand Down Expand Up @@ -978,7 +979,7 @@ func (c *DefaultCtx) Locals(key any, value ...any) any {
// All the values are removed from ctx after returning from the top
// RequestHandler. Additionally, Close method is called on each value
// implementing io.Closer before removing the value from ctx.
func Locals[V any](c Ctx, key any, value ...V) V {
func Locals[V any](c Ctx[any], key any, value ...V) V {
var v V
var ok bool
if len(value) == 0 {
Expand Down Expand Up @@ -1114,7 +1115,7 @@ func (c *DefaultCtx) Params(key string, defaultValue ...string) string {
//
// http://example.com/id/:number -> http://example.com/id/john
// Params[int](c, "number", 0) -> returns 0 because can't parse 'john' as integer.
func Params[V GenericType](c Ctx, key string, defaultValue ...V) V {
func Params[V GenericType](c Ctx[any], key string, defaultValue ...V) V {
var v V
return genericParseType(c.Params(key), v, defaultValue...)
}
Expand Down Expand Up @@ -1236,7 +1237,7 @@ func (c *DefaultCtx) Queries() map[string]string {
// name := Query[string](c, "search") // Returns "john"
// age := Query[int](c, "age") // Returns 8
// unknown := Query[string](c, "unknown", "default") // Returns "default" since the query parameter "unknown" is not found
func Query[V GenericType](c Ctx, key string, defaultValue ...V) V {
func Query[V GenericType](c Ctx[any], key string, defaultValue ...V) V {
var v V
q := c.App().getString(c.RequestCtx().QueryArgs().Peek(key))

Expand Down Expand Up @@ -1730,7 +1731,7 @@ func (c *DefaultCtx) Stale() bool {

// Status sets the HTTP status for the response.
// This method is chainable.
func (c *DefaultCtx) Status(status int) Ctx {
func (c *DefaultCtx) Status(status int) Ctx[DefaultCtx] {
c.fasthttp.Response.SetStatusCode(status)
return c
}
Expand Down Expand Up @@ -1775,7 +1776,7 @@ func (c *DefaultCtx) String() string {
}

// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
func (c *DefaultCtx) Type(extension string, charset ...string) Ctx {
func (c *DefaultCtx) Type(extension string, charset ...string) Ctx[DefaultCtx] {
if len(charset) > 0 {
c.fasthttp.Response.Header.SetContentType(utils.GetMIME(extension) + "; charset=" + charset[0])
} else {
Expand Down
69 changes: 69 additions & 0 deletions ctx_custom_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 Github Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io

package fiber

import (
"errors"

"github.com/valyala/fasthttp"
)

type CustomCtx[T any] interface {
Ctx[T]

// Reset is a method to reset context fields by given request when to use server handlers.
Reset(fctx *fasthttp.RequestCtx)

// Methods to use with next stack.
getMethodINT() int
getIndexRoute() int
getTreePath() string
getDetectionPath() string
getPathOriginal() string
getValues() *[maxParams]string
getMatched() bool
setIndexHandler(handler int)
setIndexRoute(route int)
setMatched(matched bool)
setRoute(route *Route)
}

func NewDefaultCtx(app *App[DefaultCtx]) *DefaultCtx {
// return ctx
return &DefaultCtx{
// Set app reference
app: app,
}
}

func (app *App[TCtx]) newCtx() Ctx[TCtx] {
var c Ctx[TCtx]

if app.newCtxFunc != nil {
c = app.newCtxFunc(app)
} else {
c = NewDefaultCtx(app)
}

return c
}

// AcquireCtx retrieves a new Ctx from the pool.
func (app *App[TCtx]) AcquireCtx(fctx *fasthttp.RequestCtx) Ctx[TCtx] {
ctx, ok := app.pool.Get().(Ctx)

if !ok {
panic(errors.New("failed to type-assert to Ctx"))
}
ctx.Reset(fctx)

return ctx
}

// ReleaseCtx releases the ctx back into the pool.
func (app *App[TCtx]) ReleaseCtx(c Ctx[TCtx]) {
c.release()
app.pool.Put(c)
}
Loading

0 comments on commit aac1b97

Please sign in to comment.