-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunc.go
More file actions
200 lines (173 loc) · 5.4 KB
/
func.go
File metadata and controls
200 lines (173 loc) · 5.4 KB
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
package bind
import (
"context"
"errors"
"flag"
"fmt"
"net/http"
"net/url"
"os"
"reflect"
)
const (
TagPath = "path"
TagQuery = "query"
TagHeader = "header"
TagForm = "form"
TagEnv = "env"
TagFlag = "flag"
)
func NewRequestSuppliers(req *http.Request) ([]Supplier, error) {
ps, err := NewPathSupplier(req)
if err != nil {
return nil, fmt.Errorf("creating path supplier: %w", err)
}
qs, err := NewQuerySupplier(req.URL)
if err != nil {
return nil, fmt.Errorf("creating query supplier: %w", err)
}
hs, err := NewHeaderSupplier(req.Header)
if err != nil {
return nil, fmt.Errorf("creating header supplier: %w", err)
}
fs, err := NewFormSupplier(req)
if err != nil {
return nil, fmt.Errorf("creating form supplier: %w", err)
}
return []Supplier{ps, qs, hs, fs}, nil
}
// NewPathSupplier uses the given http.Request to supply path values.
// An example path value is the "org_id" in "/orgs/{org_id}/users".
func NewPathSupplier(req *http.Request) (Supplier, error) {
if req == nil {
return nil, errors.New("request is nil")
}
return NewFuncStringSupplier(func(_ context.Context, name string, options []string) (string, error) {
return req.PathValue(name), nil
}, TagPath), nil
}
// NewQuerySupplier uses the given url.URL to supply query values.
// An example query value is the "hello" in "/search?q=hello".
func NewQuerySupplier(url *url.URL) (Supplier, error) {
if url == nil {
return nil, errors.New("url is nil")
}
query := url.Query()
return NewFuncStringSupplier(func(_ context.Context, name string, options []string) (string, error) {
return query.Get(name), nil
}, TagQuery), nil
}
// NewHeaderSupplier uses the given http.Header to supply header values.
func NewHeaderSupplier(h http.Header) (Supplier, error) {
if h == nil {
return nil, errors.New("header is nil")
}
return NewFuncStringSupplier(func(_ context.Context, name string, options []string) (string, error) {
return h.Get(name), nil
}, TagHeader), nil
}
// NewFormSupplier uses the given http.Request to supply form values.
// For documentation on the form values, look at http.Request's FormValue
// documentation.
func NewFormSupplier(r *http.Request) (Supplier, error) {
if r == nil {
return nil, errors.New("request is nil")
}
return NewFuncStringSupplier(func(_ context.Context, name string, options []string) (string, error) {
return r.FormValue(name), nil
}, TagForm), nil
}
// NewEnvSupplier grabs values from environment variables.
func NewEnvSupplier() Supplier {
return NewFuncStringSupplier(func(_ context.Context, name string, options []string) (string, error) {
return os.Getenv(name), nil
}, TagEnv)
}
// NewFlagSupplier grabs values from a given FlagSet.
// If provided nil, it will default to the flag.CommandLine flags.
func NewFlagSupplier(fs *flag.FlagSet) Supplier {
if fs == nil {
fs = flag.CommandLine
}
m := make(map[string]string)
fs.Visit(func(f *flag.Flag) {
m[f.Name] = f.Value.String()
})
return NewFuncStringSupplier(func(ctx context.Context, name string, options []string) (string, error) {
return m[name], nil
}, TagFlag)
}
type FuncStringSupplier struct {
fn func(ctx context.Context, name string, options []string) (string, error)
kind string
}
// NewFuncStringSupplier is a helper supplier, it is used to construct other suppliers.
// It will attempt to set the string returned from the function
// into the type you are binding to, using parsing if needed.
func NewFuncStringSupplier(fn func(ctx context.Context, name string, options []string) (string, error), kind string) *FuncStringSupplier {
return &FuncStringSupplier{
fn: fn,
kind: kind,
}
}
func (p *FuncStringSupplier) Fill(ctx context.Context, value string, options []string, dst any) (bool, error) {
if p.fn == nil {
return false, nil
}
result, err := p.fn(ctx, value, options)
if err != nil {
return false, fmt.Errorf("supplier %q: %w", p.kind, err)
}
if result == "" {
return false, nil
}
setStringInto(reflect.ValueOf(dst), result)
return true, nil
}
func (p *FuncStringSupplier) Kind() string {
return p.kind
}
func (p *FuncStringSupplier) IsKind(kind string) bool {
return p.kind == kind
}
type FuncSupplier struct {
fn func(ctx context.Context, name string, options []string) (any, error)
kind string
}
// NewFuncSupplier is a helper supplier, it is used to construct other suppliers.
// It requires that the type you are binding is the same
// as the type the supplier function returns.
func NewFuncSupplier(fn func(ctx context.Context, name string, options []string) (any, error), kind string) *FuncSupplier {
return &FuncSupplier{
fn: fn,
kind: kind,
}
}
func (p *FuncSupplier) Fill(ctx context.Context, value string, options []string, dst any) (bool, error) {
if p.fn == nil {
return false, nil
}
result, err := p.fn(ctx, value, options)
if err != nil {
return false, fmt.Errorf("supplier %q: %w", p.kind, err)
}
if result == nil {
return false, nil
}
ev := reflect.ValueOf(dst)
if ev.Kind() != reflect.Ptr || ev.IsNil() {
return false, fmt.Errorf("supplier %q: dst must be a non-nil pointer", p.kind)
}
rv := reflect.ValueOf(result)
if !rv.Type().AssignableTo(ev.Elem().Type()) {
return false, fmt.Errorf("supplier %q: cannot assign %s to %s", p.kind, rv.Type().String(), ev.Elem().Type().String())
}
ev.Elem().Set(rv)
return true, nil
}
func (p *FuncSupplier) Kind() string {
return p.kind
}
func (p *FuncSupplier) IsKind(kind string) bool {
return p.kind == kind
}