Skip to content

Commit b695df6

Browse files
committed
Implement Fluent Method Chaining for Status and Type Methods Using Generics #3221
1 parent 7dc7728 commit b695df6

9 files changed

+43
-42
lines changed

app.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ import (
3535
const Version = "3.0.0-beta.4"
3636

3737
// Handler defines a function to serve HTTP requests.
38-
type Handler = func(Ctx[any]) error
38+
type Handler = func(ctx Ctx) error
3939

40-
type customCtxFunc = func(app *App[any]) CustomCtx[any]
40+
type customCtxFunc = func(app *App[Ctx]) CustomCtx[Ctx]
4141

4242
// Map is a shortcut for map[string]any, useful for JSON returns
4343
type Map map[string]any
@@ -80,7 +80,7 @@ type Storage interface {
8080
// return c.Status(code).SendString(err.Error())
8181
// }
8282
// app := fiber.New(cfg)
83-
type ErrorHandler = func(Ctx[any], error) error
83+
type ErrorHandler = func(Ctx, error) error
8484

8585
// Error represents an error that occurred while handling a request.
8686
type Error struct {
@@ -89,7 +89,7 @@ type Error struct {
8989
}
9090

9191
// App denotes the Fiber application.
92-
type App[TCtx any] struct {
92+
type App[TCtx CtxGeneric[TCtx]] struct {
9393
// Ctx pool
9494
pool sync.Pool
9595
// Fasthttp server
@@ -472,7 +472,7 @@ var DefaultMethods = []string{
472472
}
473473

474474
// DefaultErrorHandler that process return errors from handlers
475-
func DefaultErrorHandler(c Ctx[any], err error) error {
475+
func DefaultErrorHandler(c Ctx, err error) error {
476476
code := StatusInternalServerError
477477
var e *Error
478478
if errors.As(err, &e) {
@@ -493,7 +493,7 @@ func DefaultErrorHandler(c Ctx[any], err error) error {
493493
// ServerHeader: "Fiber",
494494
// })
495495
func New(config ...Config) *App[DefaultCtx] {
496-
app := newApp[any](config...)
496+
app := newApp[DefaultCtx](config...)
497497

498498
// Init app
499499
app.init()
@@ -519,7 +519,7 @@ func New(config ...Config) *App[DefaultCtx] {
519519
// Prefork: true,
520520
// ServerHeader: "Fiber",
521521
// })
522-
func NewWithCustomCtx[TCtx Ctx[TCtx]](newCtxFunc customCtxFunc, config ...Config) *App[TCtx] {
522+
func NewWithCustomCtx[TCtx CtxGeneric[TCtx]](newCtxFunc customCtxFunc, config ...Config) *App[TCtx] {
523523
app := newApp[TCtx](config...)
524524

525525
// Set newCtxFunc
@@ -532,7 +532,7 @@ func NewWithCustomCtx[TCtx Ctx[TCtx]](newCtxFunc customCtxFunc, config ...Config
532532
}
533533

534534
// newApp creates a new Fiber named instance.
535-
func newApp[TCtx Ctx[TCtx]](config ...Config) *App[TCtx] {
535+
func newApp[TCtx CtxGeneric[TCtx]](config ...Config) *App[TCtx] {
536536
// Create a new app
537537
app := &App[TCtx]{
538538
// Create config
@@ -1129,7 +1129,7 @@ func (app *App[TCtx]) init() *App[TCtx] {
11291129
// sub fibers by their prefixes and if it finds a match, it uses that
11301130
// error handler. Otherwise it uses the configured error handler for
11311131
// the app, which if not set is the DefaultErrorHandler.
1132-
func (app *App[TCtx]) ErrorHandler(ctx Ctx[TCtx], err error) error {
1132+
func (app *App[TCtx]) ErrorHandler(ctx CtxGeneric[TCtx], err error) error {
11331133
var (
11341134
mountedErrHandler ErrorHandler
11351135
mountedPrefixParts int

bind.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
type CustomBinder interface {
1010
Name() string
1111
MIMETypes() []string
12-
Parse(c Ctx[any], out any) error
12+
Parse(c Ctx, out any) error
1313
}
1414

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

2020
// Bind struct
2121
type Bind struct {
22-
ctx Ctx[any]
22+
ctx Ctx
2323
dontHandleErrs bool
2424
}
2525

ctx.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ const userContextKey contextKey = 0 // __local_user_context__
4747
// generation tool `go install github.com/vburenin/ifacemaker@975a95966976eeb2d4365a7fb236e274c54da64c`
4848
// https://github.com/vburenin/ifacemaker/blob/975a95966976eeb2d4365a7fb236e274c54da64c/ifacemaker.go#L14-L30
4949
//
50-
//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."
50+
//go:generate ifacemaker --file ctx.go --struct DefaultCtx --iface CtxGeneric --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."
5151
//go:generate go run ctx_interface_gen.go
5252
type DefaultCtx struct {
53-
app *App[any] // Reference to *App
53+
app *App[*DefaultCtx] // Reference to *App
5454
route *Route // Reference to *Route
5555
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
5656
bind *Bind // Default bind reference
@@ -72,6 +72,8 @@ type DefaultCtx struct {
7272
matched bool // Non use route matched
7373
}
7474

75+
type Ctx = CtxGeneric[*DefaultCtx]
76+
7577
// SendFile defines configuration options when to transfer file with SendFile.
7678
type SendFile struct {
7779
// FS is the file system to serve the static files from.
@@ -198,7 +200,7 @@ type Views interface {
198200

199201
// ResFmt associates a Content Type to a fiber.Handler for c.Format
200202
type ResFmt struct {
201-
Handler func(Ctx[any]) error
203+
Handler func(Ctx) error
202204
MediaType string
203205
}
204206

@@ -223,7 +225,7 @@ func (c *DefaultCtx) AcceptsLanguages(offers ...string) string {
223225
}
224226

225227
// App returns the *App reference to the instance of the Fiber application
226-
func (c *DefaultCtx) App() *App[DefaultCtx] {
228+
func (c *DefaultCtx) App() *App[*DefaultCtx] {
227229
return c.app
228230
}
229231

@@ -643,7 +645,7 @@ func (c *DefaultCtx) Get(key string, defaultValue ...string) string {
643645

644646
// GetReqHeader returns the HTTP request header specified by filed.
645647
// This function is generic and can handle different headers type values.
646-
func GetReqHeader[V GenericType](c Ctx[any], key string, defaultValue ...V) V {
648+
func GetReqHeader[V GenericType](c Ctx, key string, defaultValue ...V) V {
647649
var v V
648650
return genericParseType[V](c.App().getString(c.Request().Header.Peek(key)), v, defaultValue...)
649651
}
@@ -979,7 +981,7 @@ func (c *DefaultCtx) Locals(key any, value ...any) any {
979981
// All the values are removed from ctx after returning from the top
980982
// RequestHandler. Additionally, Close method is called on each value
981983
// implementing io.Closer before removing the value from ctx.
982-
func Locals[V any](c Ctx[any], key any, value ...V) V {
984+
func Locals[V any](c Ctx, key any, value ...V) V {
983985
var v V
984986
var ok bool
985987
if len(value) == 0 {
@@ -1115,7 +1117,7 @@ func (c *DefaultCtx) Params(key string, defaultValue ...string) string {
11151117
//
11161118
// http://example.com/id/:number -> http://example.com/id/john
11171119
// Params[int](c, "number", 0) -> returns 0 because can't parse 'john' as integer.
1118-
func Params[V GenericType](c Ctx[any], key string, defaultValue ...V) V {
1120+
func Params[V GenericType](c Ctx, key string, defaultValue ...V) V {
11191121
var v V
11201122
return genericParseType(c.Params(key), v, defaultValue...)
11211123
}
@@ -1237,7 +1239,7 @@ func (c *DefaultCtx) Queries() map[string]string {
12371239
// name := Query[string](c, "search") // Returns "john"
12381240
// age := Query[int](c, "age") // Returns 8
12391241
// unknown := Query[string](c, "unknown", "default") // Returns "default" since the query parameter "unknown" is not found
1240-
func Query[V GenericType](c Ctx[any], key string, defaultValue ...V) V {
1242+
func Query[V GenericType](c Ctx, key string, defaultValue ...V) V {
12411243
var v V
12421244
q := c.App().getString(c.RequestCtx().QueryArgs().Peek(key))
12431245

@@ -1731,7 +1733,7 @@ func (c *DefaultCtx) Stale() bool {
17311733

17321734
// Status sets the HTTP status for the response.
17331735
// This method is chainable.
1734-
func (c *DefaultCtx) Status(status int) Ctx[DefaultCtx] {
1736+
func (c *DefaultCtx) Status(status int) *DefaultCtx {
17351737
c.fasthttp.Response.SetStatusCode(status)
17361738
return c
17371739
}
@@ -1776,7 +1778,7 @@ func (c *DefaultCtx) String() string {
17761778
}
17771779

17781780
// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
1779-
func (c *DefaultCtx) Type(extension string, charset ...string) Ctx[DefaultCtx] {
1781+
func (c *DefaultCtx) Type(extension string, charset ...string) *DefaultCtx {
17801782
if len(charset) > 0 {
17811783
c.fasthttp.Response.Header.SetContentType(utils.GetMIME(extension) + "; charset=" + charset[0])
17821784
} else {

ctx_custom_interface.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
type CustomCtx[T any] interface {
14-
Ctx[T]
14+
CtxGeneric[T]
1515

1616
// Reset is a method to reset context fields by given request when to use server handlers.
1717
Reset(fctx *fasthttp.RequestCtx)
@@ -30,16 +30,16 @@ type CustomCtx[T any] interface {
3030
setRoute(route *Route)
3131
}
3232

33-
func NewDefaultCtx(app *App[DefaultCtx]) *DefaultCtx {
33+
func NewDefaultCtx(app *App[*DefaultCtx]) *DefaultCtx {
3434
// return ctx
3535
return &DefaultCtx{
3636
// Set app reference
3737
app: app,
3838
}
3939
}
4040

41-
func (app *App[TCtx]) newCtx() Ctx[TCtx] {
42-
var c Ctx[TCtx]
41+
func (app *App[TCtx]) newCtx() CtxGeneric[TCtx] {
42+
var c CtxGeneric[TCtx]
4343

4444
if app.newCtxFunc != nil {
4545
c = app.newCtxFunc(app)
@@ -51,8 +51,8 @@ func (app *App[TCtx]) newCtx() Ctx[TCtx] {
5151
}
5252

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

5757
if !ok {
5858
panic(errors.New("failed to type-assert to Ctx"))
@@ -63,7 +63,7 @@ func (app *App[TCtx]) AcquireCtx(fctx *fasthttp.RequestCtx) Ctx[TCtx] {
6363
}
6464

6565
// ReleaseCtx releases the ctx back into the pool.
66-
func (app *App[TCtx]) ReleaseCtx(c Ctx[TCtx]) {
66+
func (app *App[TCtx]) ReleaseCtx(c CtxGeneric[TCtx]) {
6767
c.release()
6868
app.pool.Put(c)
6969
}

ctx_interface.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ctx_interface_gen.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ func patchCtxFile(input []byte) ([]byte, error) {
4040
scanner := bufio.NewScanner(in)
4141
var outBuf bytes.Buffer
4242

43-
regexCtx := regexp.MustCompile(`\bCtx\b`)
44-
regexApp := regexp.MustCompile(`\*\bApp\b`)
43+
regexCtx := regexp.MustCompile(`(\*Default)Ctx`)
44+
regexApp := regexp.MustCompile(`\*App(\[\w+])?`)
4545

4646
for scanner.Scan() {
4747
line := scanner.Text()
@@ -50,8 +50,8 @@ func patchCtxFile(input []byte) ([]byte, error) {
5050
// => "type Ctx interface {" -> "type Ctx[T any] interface {"
5151
if strings.HasPrefix(line, "type") {
5252
line = strings.Replace(line,
53-
"type Ctx interface {",
54-
"type Ctx[T any] interface {",
53+
"type CtxGeneric interface {",
54+
"type CtxGeneric[T any] interface {",
5555
1,
5656
)
5757
} else {
@@ -64,7 +64,6 @@ func patchCtxFile(input []byte) ([]byte, error) {
6464

6565
// C) App with generic type
6666
if strings.Contains(line, "App") {
67-
// TODO: check this part
6867
line = regexApp.ReplaceAllString(line, "*App[T]")
6968
}
7069
}

group.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
// Group struct
1313
type Group struct {
14-
app *App[any]
14+
app *App[Ctx]
1515
parentGroup *Group
1616
name string
1717

hooks.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ type (
1313
OnListenHandler = func(ListenData) error
1414
OnShutdownHandler = func() error
1515
OnForkHandler = func(int) error
16-
OnMountHandler = func(*App[any]) error
16+
OnMountHandler = func(*App[Ctx]) error
1717
)
1818

1919
// Hooks is a struct to use it with App.
2020
type Hooks struct {
2121
// Embed app
22-
app *App[any]
22+
app *App[Ctx]
2323

2424
// Hooks
2525
onRoute []OnRouteHandler
@@ -39,7 +39,7 @@ type ListenData struct {
3939
TLS bool
4040
}
4141

42-
func newHooks(app *App[Ctx[any]]) *Hooks {
42+
func newHooks(app *App[Ctx]) *Hooks {
4343
return &Hooks{
4444
app: app,
4545
onRoute: make([]OnRouteHandler, 0),
@@ -207,7 +207,7 @@ func (h *Hooks) executeOnForkHooks(pid int) {
207207
}
208208
}
209209

210-
func (h *Hooks) executeOnMountHooks(app *App[Ctx[any]]) error {
210+
func (h *Hooks) executeOnMountHooks(app *App[Ctx]) error {
211211
for _, v := range h.onMount {
212212
if err := v(app); err != nil {
213213
return err

mount.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
// Put fields related to mounting.
1616
type mountFields struct {
1717
// Mounted and main apps
18-
appList map[string]*App[any]
18+
appList map[string]*App[Ctx]
1919
// Prefix of app if it was mounted
2020
mountPath string
2121
// Ordered keys of apps (sorted by key length for Render)
@@ -68,7 +68,7 @@ func (app *App[TCtx]) mount(prefix string, subApp *App[TCtx]) Router {
6868
// Mount attaches another app instance as a sub-router along a routing path.
6969
// It's very useful to split up a large API as many independent routers and
7070
// compose them as a single service using Mount.
71-
func (grp *Group) mount(prefix string, subApp *App[Ctx[any]]) Router {
71+
func (grp *Group) mount(prefix string, subApp *App[Ctx]) Router {
7272
groupPath := getGroupPath(grp.Prefix, prefix)
7373
groupPath = utils.TrimRight(groupPath, '/')
7474
if groupPath == "" {

0 commit comments

Comments
 (0)