-
Notifications
You must be signed in to change notification settings - Fork 3
/
hwy.go
276 lines (247 loc) · 6.53 KB
/
hwy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package hwy
import (
"fmt"
"net/http"
"reflect"
"github.com/sjc5/hwy/packages/go/router"
"github.com/sjc5/kit/pkg/validate"
)
type BuildOptions = router.BuildOptions
type TSGenOptions = router.TSGenOptions
type AdHocType = router.AdHocType
type DataFuncs = router.DataFuncs
type Hwy = router.Hwy
type HeadBlock = router.HeadBlock
type DataFunctionMap = router.DataFunctionMap
type Path = router.Path
type PathsFile = router.PathsFile
type Redirect = router.Redirect
type Params = router.Params
type SplatSegments = router.SplatSegments
type RouteType = router.RouteType
type ResponseHelper = router.ResponseHelper
type CtxHelper = router.CtxHelper
var Build = router.Build
var GenerateTypeScript = router.GenerateTypeScript
var GetIsJSONRequest = router.GetIsJSONRequest
var GetHeadElements = router.GetHeadElements
var GetSSRInnerHTML = router.GetSSRInnerHTML
var RouteTypesEnum = router.RouteTypesEnum
var GetAdHocDataContextWithValue = router.GetAdHocDataContextWithValue
var CreatePublicURLResolverPlugin = router.CreatePublicURLResolverPlugin
var CreateCSSURLFuncResolverPlugin = router.CreateCSSURLFuncResolverPlugin
var HwyPathsFileName = router.HwyPathsFileName
func GetAdHocDataFromContext[T any](r *http.Request) T {
return router.GetAdHocDataFromContext[T](r)
}
type LoaderRes[O any] struct {
// same as ActionRes
Data O
ErrMsg string
Headers http.Header
Cookies []*http.Cookie
redirect *Redirect
clientRedirectURL string
// different from ActionRes
HeadBlocks []*HeadBlock
}
type LoaderCtx[O any] struct {
Req *http.Request
Params Params
SplatSegments SplatSegments
Res *LoaderRes[O]
}
func (c *LoaderCtx[O]) GetRequest() *http.Request {
return c.Req
}
func (c *LoaderCtx[O]) GetResponse() ResponseHelper {
return c.Res
}
func (f *LoaderRes[O]) ServerError() {
f.ErrMsg = http.StatusText(http.StatusInternalServerError)
}
func (f *LoaderRes[O]) Redirect(url string, code int) {
f.redirect = &Redirect{URL: url, Code: code}
}
func (f *LoaderRes[O]) ClientRedirect(url string) {
f.clientRedirectURL = url
}
type Loader[O any] func(ctx LoaderCtx[O])
func NewLoader[O any](f func(ctx LoaderCtx[O])) Loader[O] {
return Loader[O](f)
}
func (f Loader[O]) GetResInstance() any {
return &LoaderRes[O]{
redirect: &Redirect{},
Headers: http.Header{},
Cookies: []*http.Cookie{},
HeadBlocks: []*HeadBlock{},
}
}
func (f Loader[O]) Execute(args ...any) (any, error) {
f(LoaderCtx[O]{
Req: args[0].(*http.Request),
Params: args[1].(Params),
SplatSegments: args[2].(SplatSegments),
Res: args[3].(*LoaderRes[O]),
})
return nil, nil
}
func (f Loader[O]) GetInputInstance() any {
return nil
}
func (f Loader[O]) ValidateInput(v *validate.Validate, r *http.Request, actionType RouteType) (any, error) {
return nil, nil
}
func (f Loader[O]) GetOutputInstance() any {
var x O
return x
}
type ActionRes[O any] struct {
// same as LoaderRes
Data O
ErrMsg string
Headers http.Header
Cookies []*http.Cookie
redirect *Redirect
clientRedirectURL string
}
type ActionCtx[I any, O any] struct {
Req *http.Request
Input I
Res *ActionRes[O]
ResponseWriter http.ResponseWriter
}
func (c *ActionCtx[I, O]) GetRequest() *http.Request {
return c.Req
}
func (c *ActionCtx[I, O]) GetResponse() ResponseHelper {
return c.Res
}
func (f *ActionRes[O]) ServerError() {
f.ErrMsg = http.StatusText(http.StatusInternalServerError)
}
func (f *ActionRes[O]) Redirect(url string, code int) {
f.redirect = &Redirect{URL: url, Code: code}
}
func (f *ActionRes[O]) ClientRedirect(url string) {
f.clientRedirectURL = url
}
type Action[I any, O any] func(ctx ActionCtx[I, O])
func NewAction[I any, O any](f func(ctx ActionCtx[I, O])) Action[I, O] {
return Action[I, O](f)
}
func (f Action[I, O]) GetResInstance() any {
return &ActionRes[O]{
redirect: &Redirect{},
Headers: http.Header{},
Cookies: []*http.Cookie{},
}
}
func (f Action[I, O]) Execute(args ...any) (any, error) {
x := ActionCtx[I, O]{
Req: args[0].(*http.Request),
Res: args[2].(*ActionRes[O]),
ResponseWriter: args[3].(http.ResponseWriter),
}
if args[1] != nil {
x.Input = args[1].(I)
}
f(x)
return nil, nil
}
func (f Action[I, O]) GetInputInstance() any {
var x I
return x
}
func (f Action[I, O]) ValidateInput(v *validate.Validate, r *http.Request, actionType RouteType) (any, error) {
isQuery := actionType == RouteTypesEnum.QueryAction
isMutation := actionType == RouteTypesEnum.MutationAction
if !isQuery && !isMutation {
return nil, fmt.Errorf("method not accepted")
}
var x I
var err error
// __TODO add a test for an "any" input type, which comes up here as nil
_type := reflect.TypeOf(x)
if _type == nil {
return nil, nil
}
kind := _type.Kind()
isPtr := kind == reflect.Ptr
if !isPtr {
if isQuery {
err = v.URLSearchParamsInto(r, &x)
} else {
err = v.JSONBodyInto(r.Body, &x)
}
if err != nil {
return nil, err
}
return x, nil
}
ptrIsNil := reflect.ValueOf(x).IsNil()
if ptrIsNil {
x = reflect.New(_type.Elem()).Interface().(I)
}
pointsToStruct := isPtr && _type.Elem().Kind() == reflect.Struct
if pointsToStruct {
if isQuery {
err = v.URLSearchParamsInto(r, x)
} else {
err = v.JSONBodyInto(r.Body, x)
}
if err != nil {
return nil, err
}
return x, nil
}
return nil, fmt.Errorf("type I is not a struct or a pointer to a struct")
}
func (f Action[I, O]) GetOutputInstance() any {
var x O
return x
}
//////////////////// ResponseHelper ////////////////////
func (f *LoaderRes[O]) GetData() any {
return f.Data
}
func (f *LoaderRes[O]) GetErrMsg() string {
return f.ErrMsg
}
func (f *LoaderRes[O]) GetHeaders() http.Header {
return f.Headers
}
func (f *LoaderRes[O]) GetCookies() []*http.Cookie {
return f.Cookies
}
func (f *LoaderRes[O]) GetRedirect() *Redirect {
return f.redirect
}
func (f *LoaderRes[O]) GetClientRedirectURL() string {
return f.clientRedirectURL
}
func (f *LoaderRes[O]) GetHeadBlocks() []*HeadBlock {
return f.HeadBlocks
}
func (f *ActionRes[O]) GetData() any {
return f.Data
}
func (f *ActionRes[O]) GetErrMsg() string {
return f.ErrMsg
}
func (f *ActionRes[O]) GetHeaders() http.Header {
return f.Headers
}
func (f *ActionRes[O]) GetCookies() []*http.Cookie {
return f.Cookies
}
func (f *ActionRes[O]) GetRedirect() *Redirect {
return f.redirect
}
func (f *ActionRes[O]) GetClientRedirectURL() string {
return f.clientRedirectURL
}
func (f *ActionRes[O]) GetHeadBlocks() []*HeadBlock {
return nil // noop
}