Skip to content

Commit bca391d

Browse files
authored
feat: make kid configurable (#36)
1 parent ad6f367 commit bca391d

File tree

6 files changed

+299
-14
lines changed

6 files changed

+299
-14
lines changed

Diff for: kid.go

+30-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package kid
33
import (
44
"net/http"
55
"net/url"
6+
"reflect"
67
"sync"
78

89
htmlrenderer "github.com/mojixcoder/kid/html_renderer"
@@ -69,9 +70,7 @@ func (k *Kid) Run(address ...string) error {
6970

7071
// Use registers a new middleware. The middleware will be applied to all of the routes.
7172
func (k *Kid) Use(middleware MiddlewareFunc) {
72-
if middleware == nil {
73-
panic("middleware cannot be nil")
74-
}
73+
panicIfNil(middleware, "middleware cannot be nil")
7574

7675
k.middlewares = append(k.middlewares, middleware)
7776
}
@@ -209,11 +208,6 @@ func (k *Kid) ServeHTTP(w http.ResponseWriter, r *http.Request) {
209208
k.pool.Put(c)
210209
}
211210

212-
// Debug returns whether we are in debug mode or not.
213-
func (k *Kid) Debug() bool {
214-
return k.debug
215-
}
216-
217211
// applyMiddlewaresToHandler applies middlewares to the handler and returns the handler.
218212
func (k *Kid) applyMiddlewaresToHandler(handler HandlerFunc, middlewares ...MiddlewareFunc) HandlerFunc {
219213
for i := len(middlewares) - 1; i >= 0; i-- {
@@ -222,6 +216,20 @@ func (k *Kid) applyMiddlewaresToHandler(handler HandlerFunc, middlewares ...Midd
222216
return handler
223217
}
224218

219+
// Debug returns whether we are in debug mode or not.
220+
func (k *Kid) Debug() bool {
221+
return k.debug
222+
}
223+
224+
// ApplyOptions applies the given options.
225+
func (k *Kid) ApplyOptions(opts ...Option) {
226+
for _, opt := range opts {
227+
panicIfNil(opt, "option cannot be nil")
228+
229+
opt.apply(k)
230+
}
231+
}
232+
225233
// getPath returns request's path.
226234
func getPath(u *url.URL) string {
227235
if u.RawPath != "" {
@@ -237,3 +245,17 @@ func resolveAddress(addresses []string) string {
237245
}
238246
return addresses[0]
239247
}
248+
249+
// panicIfNil panics if the given parameter is nil.
250+
func panicIfNil(x any, message string) {
251+
if x == nil {
252+
panic(message)
253+
}
254+
255+
switch reflect.TypeOf(x).Kind() {
256+
case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice, reflect.Interface, reflect.Func:
257+
if reflect.ValueOf(x).IsNil() {
258+
panic(message)
259+
}
260+
}
261+
}

Diff for: kid_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,63 @@ func TestGetPath(t *testing.T) {
623623
assert.NotEmpty(t, u.RawPath)
624624
assert.Equal(t, u.RawPath, getPath(u))
625625
}
626+
627+
func TestApplyOptions(t *testing.T) {
628+
k := New()
629+
630+
assert.PanicsWithValue(t, "option cannot be nil", func() {
631+
k.ApplyOptions(nil)
632+
})
633+
634+
handler := func(c *Context, err error) {
635+
}
636+
637+
k.ApplyOptions(
638+
WithDebug(true),
639+
WithErrorHandler(handler),
640+
)
641+
642+
assert.True(t, k.Debug())
643+
assert.True(t, funcsAreEqual(handler, k.errorHandler))
644+
}
645+
646+
func TestPanicIfNil(t *testing.T) {
647+
assert.PanicsWithValue(t, "nil", func() {
648+
panicIfNil(nil, "nil")
649+
})
650+
651+
assert.Panics(t, func() {
652+
var x *int
653+
panicIfNil(x, "")
654+
})
655+
656+
assert.Panics(t, func() {
657+
var x []string
658+
panicIfNil(x, "")
659+
})
660+
661+
assert.Panics(t, func() {
662+
var x map[string]string
663+
panicIfNil(x, "")
664+
})
665+
666+
assert.Panics(t, func() {
667+
var x interface{}
668+
panicIfNil(x, "")
669+
})
670+
671+
assert.Panics(t, func() {
672+
var x func()
673+
panicIfNil(x, "")
674+
})
675+
676+
assert.Panics(t, func() {
677+
var x chan bool
678+
panicIfNil(x, "")
679+
})
680+
681+
assert.Panics(t, func() {
682+
var x [1]int
683+
panicIfNil(x, "")
684+
})
685+
}

Diff for: mux.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ func (router *Router) add(path string, handler HandlerFunc, methods []string, mi
5757
panic("providing at least one method is required")
5858
}
5959

60-
if handler == nil {
61-
panic("handler cannot be nil")
62-
}
60+
panicIfNil(handler, "handler cannot be nil")
6361

6462
path = cleanPath(path, false)
6563

Diff for: options.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package kid
2+
3+
import (
4+
htmlrenderer "github.com/mojixcoder/kid/html_renderer"
5+
"github.com/mojixcoder/kid/serializer"
6+
)
7+
8+
type (
9+
// Option is the interface for customizing Kid.
10+
Option interface {
11+
apply(*Kid)
12+
}
13+
14+
optionImpl func(*Kid)
15+
)
16+
17+
// applyOption implements the Option interface.
18+
func (f optionImpl) apply(k *Kid) {
19+
f(k)
20+
}
21+
22+
// WithDebug configures Kid's debug option.
23+
func WithDebug(debug bool) Option {
24+
return optionImpl(func(k *Kid) {
25+
k.debug = debug
26+
})
27+
}
28+
29+
// WithHTMLRenderer configures Kid's HTML renderer.
30+
func WithHTMLRenderer(renderer htmlrenderer.HTMLRenderer) Option {
31+
panicIfNil(renderer, "renderer cannot be nil")
32+
33+
return optionImpl(func(k *Kid) {
34+
k.htmlRenderer = renderer
35+
})
36+
}
37+
38+
// WithXMLSerializer configures Kid's XML serializer.
39+
func WithXMLSerializer(serializer serializer.Serializer) Option {
40+
panicIfNil(serializer, "xml serializer cannot be nil")
41+
42+
return optionImpl(func(k *Kid) {
43+
k.xmlSerializer = serializer
44+
})
45+
}
46+
47+
// WithJSONSerializer configures Kid's JSON serializer.
48+
func WithJSONSerializer(serializer serializer.Serializer) Option {
49+
panicIfNil(serializer, "json serializer cannot be nil")
50+
51+
return optionImpl(func(k *Kid) {
52+
k.jsonSerializer = serializer
53+
})
54+
}
55+
56+
// WithErrorHandler configures Kid's error handler.
57+
func WithErrorHandler(errHandler ErrorHandler) Option {
58+
panicIfNil(errHandler, "error handler cannot be nil")
59+
60+
return optionImpl(func(k *Kid) {
61+
k.errorHandler = errHandler
62+
})
63+
}
64+
65+
// WithNotFoundHandler configures Kid's not found handler.
66+
func WithNotFoundHandler(handler HandlerFunc) Option {
67+
panicIfNil(handler, "not found handler cannot be nil")
68+
69+
return optionImpl(func(k *Kid) {
70+
k.notFoundHandler = handler
71+
})
72+
}
73+
74+
// WithMethodNotAllowedHandler configures Kid's method not allowed handler.
75+
func WithMethodNotAllowedHandler(handler HandlerFunc) Option {
76+
panicIfNil(handler, "method not allowed handler cannot be nil")
77+
78+
return optionImpl(func(k *Kid) {
79+
k.methodNotAllowedHandler = handler
80+
})
81+
}

Diff for: options_test.go

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package kid
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
type mockEverything struct{}
11+
12+
func (*mockEverything) RenderHTML(res http.ResponseWriter, path string, data any) error {
13+
return nil
14+
}
15+
16+
func (*mockEverything) Write(w http.ResponseWriter, in any, indent string) error {
17+
return nil
18+
}
19+
20+
func (*mockEverything) Read(req *http.Request, out any) error {
21+
return nil
22+
}
23+
24+
func TestWithDebug(t *testing.T) {
25+
k := New()
26+
27+
opt := WithDebug(true)
28+
opt.apply(k)
29+
30+
assert.True(t, k.Debug())
31+
}
32+
33+
func TestWithHTMLRenderer(t *testing.T) {
34+
k := New()
35+
36+
assert.PanicsWithValue(t, "renderer cannot be nil", func() {
37+
WithHTMLRenderer(nil)
38+
})
39+
40+
renderer := &mockEverything{}
41+
42+
opt := WithHTMLRenderer(renderer)
43+
opt.apply(k)
44+
45+
assert.Equal(t, renderer, k.htmlRenderer)
46+
}
47+
48+
func TestWithXMLSerializer(t *testing.T) {
49+
k := New()
50+
51+
assert.PanicsWithValue(t, "xml serializer cannot be nil", func() {
52+
WithXMLSerializer(nil)
53+
})
54+
55+
serializer := &mockEverything{}
56+
57+
opt := WithXMLSerializer(serializer)
58+
opt.apply(k)
59+
60+
assert.Equal(t, serializer, k.xmlSerializer)
61+
}
62+
63+
func TestWithJSONSerializer(t *testing.T) {
64+
k := New()
65+
66+
assert.PanicsWithValue(t, "json serializer cannot be nil", func() {
67+
WithJSONSerializer(nil)
68+
})
69+
70+
serializer := &mockEverything{}
71+
72+
opt := WithJSONSerializer(serializer)
73+
opt.apply(k)
74+
75+
assert.Equal(t, serializer, k.jsonSerializer)
76+
}
77+
78+
func TestWithErrorHandler(t *testing.T) {
79+
k := New()
80+
81+
assert.PanicsWithValue(t, "error handler cannot be nil", func() {
82+
WithErrorHandler(nil)
83+
})
84+
85+
hanlder := func(c *Context, err error) {
86+
}
87+
88+
opt := WithErrorHandler(hanlder)
89+
opt.apply(k)
90+
91+
assert.True(t, funcsAreEqual(hanlder, k.errorHandler))
92+
}
93+
94+
func TestWithNotFoundHandler(t *testing.T) {
95+
k := New()
96+
97+
assert.PanicsWithValue(t, "not found handler cannot be nil", func() {
98+
WithNotFoundHandler(nil)
99+
})
100+
101+
hanlder := func(c *Context) error {
102+
return nil
103+
}
104+
105+
opt := WithNotFoundHandler(hanlder)
106+
opt.apply(k)
107+
108+
assert.True(t, funcsAreEqual(hanlder, k.notFoundHandler))
109+
}
110+
111+
func TestWithMethodNotAllowedHandler(t *testing.T) {
112+
k := New()
113+
114+
assert.PanicsWithValue(t, "method not allowed handler cannot be nil", func() {
115+
WithMethodNotAllowedHandler(nil)
116+
})
117+
118+
hanlder := func(c *Context) error {
119+
return nil
120+
}
121+
122+
opt := WithMethodNotAllowedHandler(hanlder)
123+
opt.apply(k)
124+
125+
assert.True(t, funcsAreEqual(hanlder, k.methodNotAllowedHandler))
126+
}

Diff for: static.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ func (fs FS) Open(name string) (http.File, error) {
3434

3535
// newFileServer returns new file server.
3636
func newFileServer(urlPath string, fs http.FileSystem) http.Handler {
37-
if fs == nil {
38-
panic("file system cannot be nil")
39-
}
37+
panicIfNil(fs, "file system cannot be nil")
4038

4139
urlPath = cleanPath(urlPath, false)
4240
urlPath = appendSlash(urlPath)

0 commit comments

Comments
 (0)