Skip to content

Commit 67965e6

Browse files
committed
Implement Fluent Method Chaining for Status and Type Methods Using Generics #3221
1 parent fadbb0a commit 67965e6

14 files changed

+230
-324
lines changed

app.go

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

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

4040
// Map is a shortcut for map[string]any, useful for JSON returns
4141
type Map map[string]any
@@ -78,7 +78,7 @@ type Storage interface {
7878
// return c.Status(code).SendString(err.Error())
7979
// }
8080
// app := fiber.New(cfg)
81-
type ErrorHandler = func(Ctx, error) error
81+
type ErrorHandler[TCtx CtxGeneric[TCtx]] = func(TCtx, error) error
8282

8383
// Error represents an error that occurred while handling a request.
8484
type Error struct {
@@ -97,29 +97,29 @@ type App[TCtx CtxGeneric[TCtx]] struct {
9797
// Converts byte slice to a string
9898
getString func(b []byte) string
9999
// Hooks
100-
hooks *Hooks
100+
hooks *Hooks[TCtx]
101101
// Latest route & group
102-
latestRoute *Route
102+
latestRoute *Route[TCtx]
103103
// newCtxFunc
104104
newCtxFunc func(app *App[TCtx]) CustomCtx[TCtx]
105105
// TLS handler
106106
tlsHandler *TLSHandler
107107
// Mount fields
108-
mountFields *mountFields
108+
mountFields *mountFields[TCtx]
109109
// Route stack divided by HTTP methods
110-
stack [][]*Route
110+
stack [][]*Route[TCtx]
111111
// Route stack divided by HTTP methods and route prefixes
112-
treeStack []map[string][]*Route
112+
treeStack []map[string][]*Route[TCtx]
113113
// custom binders
114114
customBinders []CustomBinder
115115
// customConstraints is a list of external constraints
116116
customConstraints []CustomConstraint
117117
// sendfiles stores configurations for handling ctx.SendFile operations
118118
sendfiles []*sendFileStore
119119
// App config
120-
config Config
120+
config Config[TCtx]
121121
// Indicates if the value was explicitly configured
122-
configured Config
122+
configured Config[TCtx]
123123
// sendfilesMutex is a mutex used for sendfile operations
124124
sendfilesMutex sync.RWMutex
125125
mutex sync.Mutex
@@ -132,7 +132,7 @@ type App[TCtx CtxGeneric[TCtx]] struct {
132132
}
133133

134134
// Config is a struct holding the server settings.
135-
type Config struct { //nolint:govet // Aligning the struct fields is not necessary. betteralign:ignore
135+
type Config[TCtx CtxGeneric[TCtx]] struct { //nolint:govet // Aligning the struct fields is not necessary. betteralign:ignore
136136
// Enables the "Server: value" HTTP header.
137137
//
138138
// Default: ""
@@ -250,7 +250,7 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
250250
// ErrorHandler is executed when an error is returned from fiber.Handler.
251251
//
252252
// Default: DefaultErrorHandler
253-
ErrorHandler ErrorHandler `json:"-"`
253+
ErrorHandler ErrorHandler[TCtx] `json:"-"`
254254

255255
// When set to true, disables keep-alive connections.
256256
// The server will close incoming connections after sending the first response to client.
@@ -470,7 +470,7 @@ var DefaultMethods = []string{
470470
}
471471

472472
// DefaultErrorHandler that process return errors from handlers
473-
func DefaultErrorHandler(c Ctx, err error) error {
473+
func DefaultErrorHandler[TCtx CtxGeneric[TCtx]](c TCtx, err error) error {
474474
code := StatusInternalServerError
475475
var e *Error
476476
if errors.As(err, &e) {
@@ -490,7 +490,7 @@ func DefaultErrorHandler(c Ctx, err error) error {
490490
// Prefork: true,
491491
// ServerHeader: "Fiber",
492492
// })
493-
func New(config ...Config) *App[*DefaultCtx] {
493+
func New(config ...Config[*DefaultCtx]) *App[*DefaultCtx] {
494494
app := newApp[*DefaultCtx](config...)
495495

496496
// Init app
@@ -517,7 +517,7 @@ func New(config ...Config) *App[*DefaultCtx] {
517517
// Prefork: true,
518518
// ServerHeader: "Fiber",
519519
// })
520-
func NewWithCustomCtx[TCtx CtxGeneric[TCtx]](newCtxFunc func(app *App[TCtx]) CustomCtx[TCtx], config ...Config) *App[TCtx] {
520+
func NewWithCustomCtx[TCtx CtxGeneric[TCtx]](newCtxFunc func(app *App[TCtx]) CustomCtx[TCtx], config ...Config[TCtx]) *App[TCtx] {
521521
app := newApp[TCtx](config...)
522522

523523
// Set newCtxFunc
@@ -530,14 +530,14 @@ func NewWithCustomCtx[TCtx CtxGeneric[TCtx]](newCtxFunc func(app *App[TCtx]) Cus
530530
}
531531

532532
// newApp creates a new Fiber named instance.
533-
func newApp[TCtx CtxGeneric[TCtx]](config ...Config) *App[TCtx] {
533+
func newApp[TCtx CtxGeneric[TCtx]](config ...Config[TCtx]) *App[TCtx] {
534534
// Create a new app
535535
app := &App[TCtx]{
536536
// Create config
537-
config: Config{},
537+
config: Config[TCtx]{},
538538
getBytes: utils.UnsafeBytes,
539539
getString: utils.UnsafeString,
540-
latestRoute: &Route{},
540+
latestRoute: &Route[TCtx]{},
541541
customBinders: []CustomBinder{},
542542
sendfiles: []*sendFileStore{},
543543
}
@@ -550,7 +550,7 @@ func newApp[TCtx CtxGeneric[TCtx]](config ...Config) *App[TCtx] {
550550
}
551551

552552
// Define hooks
553-
app.hooks = newHooks(app)
553+
app.hooks = newHooks[TCtx](app)
554554

555555
// Define mountFields
556556
app.mountFields = newMountFields(app)
@@ -589,7 +589,7 @@ func newApp[TCtx CtxGeneric[TCtx]](config ...Config) *App[TCtx] {
589589
}
590590

591591
if app.config.ErrorHandler == nil {
592-
app.config.ErrorHandler = DefaultErrorHandler
592+
app.config.ErrorHandler = DefaultErrorHandler[TCtx]
593593
}
594594

595595
if app.config.JSONEncoder == nil {
@@ -620,8 +620,8 @@ func newApp[TCtx CtxGeneric[TCtx]](config ...Config) *App[TCtx] {
620620
}
621621

622622
// Create router stack
623-
app.stack = make([][]*Route, len(app.config.RequestMethods))
624-
app.treeStack = make([]map[string][]*Route, len(app.config.RequestMethods))
623+
app.stack = make([][]*Route[TCtx], len(app.config.RequestMethods))
624+
app.treeStack = make([]map[string][]*Route[TCtx], len(app.config.RequestMethods))
625625

626626
// Override colors
627627
app.config.ColorScheme = defaultColors(app.config.ColorScheme)
@@ -669,7 +669,7 @@ func (app *App[TCtx]) SetTLSHandler(tlsHandler *TLSHandler) {
669669
}
670670

671671
// Name Assign name to specific route.
672-
func (app *App[TCtx]) Name(name string) Router {
672+
func (app *App[TCtx]) Name(name string) Router[TCtx] {
673673
app.mutex.Lock()
674674
defer app.mutex.Unlock()
675675

@@ -695,7 +695,7 @@ func (app *App[TCtx]) Name(name string) Router {
695695
}
696696

697697
// GetRoute Get route by name
698-
func (app *App[TCtx]) GetRoute(name string) Route {
698+
func (app *App[TCtx]) GetRoute(name string) Route[TCtx] {
699699
for _, routes := range app.stack {
700700
for _, route := range routes {
701701
if route.Name == name {
@@ -704,12 +704,12 @@ func (app *App[TCtx]) GetRoute(name string) Route {
704704
}
705705
}
706706

707-
return Route{}
707+
return Route[TCtx]{}
708708
}
709709

710710
// GetRoutes Get all routes. When filterUseOption equal to true, it will filter the routes registered by the middleware.
711-
func (app *App[TCtx]) GetRoutes(filterUseOption ...bool) []Route {
712-
var rs []Route
711+
func (app *App[TCtx]) GetRoutes(filterUseOption ...bool) []Route[TCtx] {
712+
var rs []Route[TCtx]
713713
var filterUse bool
714714
if len(filterUseOption) != 0 {
715715
filterUse = filterUseOption[0]
@@ -746,11 +746,11 @@ func (app *App[TCtx]) GetRoutes(filterUseOption ...bool) []Route {
746746
// app.Use("/mounted-path", subApp)
747747
//
748748
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
749-
func (app *App[TCtx]) Use(args ...any) Router {
749+
func (app *App[TCtx]) Use(args ...any) Router[TCtx] {
750750
var prefix string
751751
var subApp *App[TCtx]
752752
var prefixes []string
753-
var handlers []Handler
753+
var handlers []Handler[TCtx]
754754

755755
for i := 0; i < len(args); i++ {
756756
switch arg := args[i].(type) {
@@ -760,7 +760,7 @@ func (app *App[TCtx]) Use(args ...any) Router {
760760
subApp = arg
761761
case []string:
762762
prefixes = arg
763-
case Handler:
763+
case Handler[TCtx]:
764764
handlers = append(handlers, arg)
765765
default:
766766
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
@@ -785,75 +785,75 @@ func (app *App[TCtx]) Use(args ...any) Router {
785785

786786
// Get registers a route for GET methods that requests a representation
787787
// of the specified resource. Requests using GET should only retrieve data.
788-
func (app *App[TCtx]) Get(path string, handler Handler, middleware ...Handler) Router {
788+
func (app *App[TCtx]) Get(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
789789
return app.Add([]string{MethodGet}, path, handler, middleware...)
790790
}
791791

792792
// Head registers a route for HEAD methods that asks for a response identical
793793
// to that of a GET request, but without the response body.
794-
func (app *App[TCtx]) Head(path string, handler Handler, middleware ...Handler) Router {
794+
func (app *App[TCtx]) Head(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
795795
return app.Add([]string{MethodHead}, path, handler, middleware...)
796796
}
797797

798798
// Post registers a route for POST methods that is used to submit an entity to the
799799
// specified resource, often causing a change in state or side effects on the server.
800-
func (app *App[TCtx]) Post(path string, handler Handler, middleware ...Handler) Router {
800+
func (app *App[TCtx]) Post(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
801801
return app.Add([]string{MethodPost}, path, handler, middleware...)
802802
}
803803

804804
// Put registers a route for PUT methods that replaces all current representations
805805
// of the target resource with the request payload.
806-
func (app *App[TCtx]) Put(path string, handler Handler, middleware ...Handler) Router {
806+
func (app *App[TCtx]) Put(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
807807
return app.Add([]string{MethodPut}, path, handler, middleware...)
808808
}
809809

810810
// Delete registers a route for DELETE methods that deletes the specified resource.
811-
func (app *App[TCtx]) Delete(path string, handler Handler, middleware ...Handler) Router {
811+
func (app *App[TCtx]) Delete(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
812812
return app.Add([]string{MethodDelete}, path, handler, middleware...)
813813
}
814814

815815
// Connect registers a route for CONNECT methods that establishes a tunnel to the
816816
// server identified by the target resource.
817-
func (app *App[TCtx]) Connect(path string, handler Handler, middleware ...Handler) Router {
817+
func (app *App[TCtx]) Connect(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
818818
return app.Add([]string{MethodConnect}, path, handler, middleware...)
819819
}
820820

821821
// Options registers a route for OPTIONS methods that is used to describe the
822822
// communication options for the target resource.
823-
func (app *App[TCtx]) Options(path string, handler Handler, middleware ...Handler) Router {
823+
func (app *App[TCtx]) Options(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
824824
return app.Add([]string{MethodOptions}, path, handler, middleware...)
825825
}
826826

827827
// Trace registers a route for TRACE methods that performs a message loop-back
828828
// test along the path to the target resource.
829-
func (app *App[TCtx]) Trace(path string, handler Handler, middleware ...Handler) Router {
829+
func (app *App[TCtx]) Trace(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
830830
return app.Add([]string{MethodTrace}, path, handler, middleware...)
831831
}
832832

833833
// Patch registers a route for PATCH methods that is used to apply partial
834834
// modifications to a resource.
835-
func (app *App[TCtx]) Patch(path string, handler Handler, middleware ...Handler) Router {
835+
func (app *App[TCtx]) Patch(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
836836
return app.Add([]string{MethodPatch}, path, handler, middleware...)
837837
}
838838

839839
// Add allows you to specify multiple HTTP methods to register a route.
840-
func (app *App[TCtx]) Add(methods []string, path string, handler Handler, middleware ...Handler) Router {
840+
func (app *App[TCtx]) Add(methods []string, path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
841841
app.register(methods, path, nil, handler, middleware...)
842842

843843
return app
844844
}
845845

846846
// All will register the handler on all HTTP methods
847-
func (app *App[TCtx]) All(path string, handler Handler, middleware ...Handler) Router {
847+
func (app *App[TCtx]) All(path string, handler Handler[TCtx], middleware ...Handler[TCtx]) Router[TCtx] {
848848
return app.Add(app.config.RequestMethods, path, handler, middleware...)
849849
}
850850

851851
// Group is used for Routes with common prefix to define a new sub-router with optional middleware.
852852
//
853853
// api := app.Group("/api")
854854
// api.Get("/users", handler)
855-
func (app *App[TCtx]) Group(prefix string, handlers ...Handler) Router {
856-
grp := &Group{Prefix: prefix, app: app}
855+
func (app *App[TCtx]) Group(prefix string, handlers ...Handler[TCtx]) Router[TCtx] {
856+
grp := &Group[TCtx]{Prefix: prefix, app: app}
857857
if len(handlers) > 0 {
858858
app.register([]string{methodUse}, prefix, grp, nil, handlers...)
859859
}
@@ -866,9 +866,9 @@ func (app *App[TCtx]) Group(prefix string, handlers ...Handler) Router {
866866

867867
// Route is used to define routes with a common prefix inside the common function.
868868
// Uses Group method to define new sub-router.
869-
func (app *App[TCtx]) Route(path string) Register {
869+
func (app *App[TCtx]) Route(path string) Register[TCtx] {
870870
// Create new route
871-
route := &Registering{app: app, path: path}
871+
route := &Registering[TCtx]{app: app, path: path}
872872

873873
return route
874874
}
@@ -891,7 +891,7 @@ func NewError(code int, message ...string) *Error {
891891
}
892892

893893
// Config returns the app config as value ( read-only ).
894-
func (app *App[TCtx]) Config() Config {
894+
func (app *App[TCtx]) Config() Config[TCtx] {
895895
return app.config
896896
}
897897

@@ -900,14 +900,11 @@ func (app *App[TCtx]) Handler() fasthttp.RequestHandler { //revive:disable-line:
900900
// prepare the server for the start
901901
app.startupProcess()
902902

903-
if app.newCtxFunc != nil {
904-
return app.customRequestHandler
905-
}
906-
return app.defaultRequestHandler
903+
return app.requestHandler
907904
}
908905

909906
// Stack returns the raw router stack.
910-
func (app *App[TCtx]) Stack() [][]*Route {
907+
func (app *App[TCtx]) Stack() [][]*Route[TCtx] {
911908
return app.stack
912909
}
913910

@@ -964,7 +961,7 @@ func (app *App[TCtx]) Server() *fasthttp.Server {
964961
}
965962

966963
// Hooks returns the hook struct to register hooks.
967-
func (app *App[TCtx]) Hooks() *Hooks {
964+
func (app *App[TCtx]) Hooks() *Hooks[TCtx] {
968965
return app.hooks
969966
}
970967

@@ -1094,11 +1091,7 @@ func (app *App[TCtx]) init() *App[TCtx] {
10941091
}
10951092

10961093
// fasthttp server settings
1097-
if app.newCtxFunc != nil {
1098-
app.server.Handler = app.customRequestHandler
1099-
} else {
1100-
app.server.Handler = app.defaultRequestHandler
1101-
}
1094+
app.server.Handler = app.requestHandler
11021095
app.server.Name = app.config.ServerHeader
11031096
app.server.Concurrency = app.config.Concurrency
11041097
app.server.NoDefaultDate = app.config.DisableDefaultDate
@@ -1127,9 +1120,9 @@ func (app *App[TCtx]) init() *App[TCtx] {
11271120
// sub fibers by their prefixes and if it finds a match, it uses that
11281121
// error handler. Otherwise it uses the configured error handler for
11291122
// the app, which if not set is the DefaultErrorHandler.
1130-
func (app *App[TCtx]) ErrorHandler(ctx CtxGeneric[TCtx], err error) error {
1123+
func (app *App[TCtx]) ErrorHandler(ctx TCtx, err error) error {
11311124
var (
1132-
mountedErrHandler ErrorHandler
1125+
mountedErrHandler ErrorHandler[TCtx]
11331126
mountedPrefixParts int
11341127
)
11351128

ctx.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const userContextKey contextKey = 0 // __local_user_context__
5151
//go:generate go run ctx_interface_gen.go
5252
type DefaultCtx struct {
5353
app *App[*DefaultCtx] // Reference to *App
54-
route *Route // Reference to *Route
54+
route *Route[*DefaultCtx] // Reference to *Route
5555
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
5656
bind *Bind // Default bind reference
5757
redirect *Redirect // Default redirect reference
@@ -1048,6 +1048,7 @@ func (c *DefaultCtx) Next() error {
10481048
}
10491049

10501050
// Continue handler stack
1051+
// TODO: reduce this with generics
10511052
if c.app.newCtxFunc != nil {
10521053
_, err := c.app.nextCustom(c)
10531054
return err
@@ -1063,6 +1064,7 @@ func (c *DefaultCtx) RestartRouting() error {
10631064
var err error
10641065

10651066
c.indexRoute = -1
1067+
// TODO: reduce this with generics
10661068
if c.app.newCtxFunc != nil {
10671069
_, err = c.app.nextCustom(c)
10681070
} else {
@@ -1978,7 +1980,7 @@ func (c *DefaultCtx) setMatched(matched bool) {
19781980
c.matched = matched
19791981
}
19801982

1981-
func (c *DefaultCtx) setRoute(route *Route) {
1983+
func (c *DefaultCtx) setRoute(route *Route[*DefaultCtx]) {
19821984
c.route = route
19831985
}
19841986

0 commit comments

Comments
 (0)