-
Notifications
You must be signed in to change notification settings - Fork 0
/
wrappers.go
125 lines (113 loc) · 2.85 KB
/
wrappers.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
package fetch
import (
"context"
"reflect"
"strings"
)
/*
Response is a wrapper type for (generic) ReturnType to be used in
the HTTP methods. It allows you to access HTTP attributes
of the HTTP response and unmarshal the HTTP body.
e.g.
type User struct {
FirstName string
}
res, err := Get[Response[User]]("/users/1")
if err != nil {panic(err)}
if res.Status != 202 {
panic("unexpected status")
}
// Body is User type
fmt.Println(res.Body.FirstName)
*/
type Response[T any] struct {
Status int
Headers map[string]string
Body T
}
func mapFlatten(m map[string][]string) map[string]string {
newM := make(map[string]string, len(m))
for key, val := range m {
if len(val) > 0 {
// it takes the last element intentionally.
newM[key] = val[len(val)-1]
}
}
return newM
}
/*
Request can be used in ApplyFunc as a wrapper
for the input entity to access http attributes.
e.g.
type Pet struct {
Name string
}
http.HandleFunc("POST /pets/{id}", fetch.ToHandlerFunc(func(in fetch.Request[Pet]) (fetch.Empty, error) {
in.Context()
return fetch.Empty{}, nil
}))
*/
type Request[T any] struct {
Context context.Context
// Only available in go1.23 and above.
// PathValue was introduced in go1.22 but
// there was no reliable way to extract them.
// go1.23 introduced http.Request.Pattern allowing to list the wildcards.
PathValues map[string]string
// URL parameters.
Parameters map[string]string
// HTTP headers.
Headers map[string]string
Body T
}
// Empty represents an empty response or request body, skipping JSON handling.
// Can be used with the wrappers Response and Request or to fit the signature of ApplyFunc.
type Empty struct{}
func isResponseWrapper(v any) bool {
if v == nil {
return false
}
typeOf := reflect.TypeOf(v)
return typeOf.PkgPath() == "github.com/glossd/fetch" && strings.HasPrefix(typeOf.Name(), "Response[")
}
func isResponseWithEmpty(v any) bool {
return reflect.TypeOf(v) == reflectTypeFor[Response[Empty]]()
}
func isRequestWrapper(v any) bool {
typeOf := reflect.TypeOf(v)
return typeOf != nil && typeOf.PkgPath() == "github.com/glossd/fetch" && strings.HasPrefix(typeOf.Name(), "Request[")
}
func isEmptyType(v any) bool {
st, ok := isStructType(v)
if !ok {
return false
}
return st == reflect.TypeOf(Empty{})
}
func isStructType(v any) (reflect.Type, bool) {
typeOf := reflect.TypeOf(v)
if v == nil {
return typeOf, false
}
switch typeOf.Kind() {
case reflect.Pointer:
valueOf := reflect.ValueOf(v)
if valueOf.IsNil() {
return typeOf, false
}
t := reflect.ValueOf(v).Elem().Type()
return t, t.Kind() == reflect.Struct
case reflect.Struct:
return typeOf, true
default:
return typeOf, false
}
}
// reflect.TypeFor was introduced in go1.22
func reflectTypeFor[T any]() reflect.Type {
var v T
if t := reflect.TypeOf(v); t != nil {
return t
}
return reflect.TypeOf((*T)(nil)).Elem()
}