forked from ggicci/httpin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdirectives.go
More file actions
171 lines (144 loc) · 5.2 KB
/
directives.go
File metadata and controls
171 lines (144 loc) · 5.2 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
package httpin
import (
"context"
"fmt"
"net/http"
"reflect"
"strings"
)
var (
executors = make(map[string]DirectiveExecutor)
normalizers = make(map[string]DirectiveNormalizer)
reservedExecutorNames = map[string]struct{}{"decoder": {}}
)
func init() {
// Built-in Directives
RegisterDirectiveExecutor("form", DirectiveExecutorFunc(formValueExtractor), nil)
RegisterDirectiveExecutor("query", DirectiveExecutorFunc(queryValueExtractor), nil)
RegisterDirectiveExecutor("header", DirectiveExecutorFunc(headerValueExtractor), nil)
RegisterDirectiveExecutor(
"body",
DirectiveExecutorFunc(bodyDecoder),
DirectiveNormalizerFunc(bodyDirectiveNormalizer),
)
RegisterDirectiveExecutor("required", DirectiveExecutorFunc(required), nil)
RegisterDirectiveExecutor("default", DirectiveExecutorFunc(defaultValueSetter), nil)
// decoder is a special executor which does nothing, but is an indicator of
// overriding the decoder for a specific field.
RegisterDirectiveExecutor("decoder", DirectiveExecutorFunc(noop), nil)
}
// DirectiveExecutor is the interface implemented by a "directive executor".
type DirectiveExecutor interface {
Execute(*DirectiveContext) error
}
type DirectiveNormalizer interface {
Normalize(*Directive) error
}
// RegisterDirectiveExecutor registers a named executor globally, which
// implemented the DirectiveExecutor interface. Will panic if the name were
// taken or nil executor.
func RegisterDirectiveExecutor(name string, exe DirectiveExecutor, norm DirectiveNormalizer) {
if _, ok := executors[name]; ok {
panic(fmt.Errorf("httpin: %w: %q", ErrDuplicateExecutor, name))
}
ReplaceDirectiveExecutor(name, exe, norm)
}
// ReplaceDirectiveExecutor works like RegisterDirectiveExecutor without panic
// on duplicate names.
func ReplaceDirectiveExecutor(name string, exe DirectiveExecutor, norm DirectiveNormalizer) {
if exe == nil {
panic(fmt.Errorf("httpin: %w: %q", ErrNilExecutor, name))
}
if _, ok := executors[name]; ok {
if _, ok := reservedExecutorNames[name]; ok {
panic(fmt.Errorf("httpin: %w: %q", ErrReservedExecutorName, name))
}
}
executors[name] = exe
normalizers[name] = norm
}
// DirectiveExecutorFunc is an adpator to allow to use of ordinary functions as
// httpin.DirectiveExecutor.
type DirectiveExecutorFunc func(*DirectiveContext) error
// Execute calls f(ctx).
func (f DirectiveExecutorFunc) Execute(ctx *DirectiveContext) error {
return f(ctx)
}
// DirectiveNormalizerFunc is an adaptor to allow to use of ordinary functions as
// httpin.DirectiveNormalizer.
type DirectiveNormalizerFunc func(*Directive) error
// Normalize calls f(dir).
func (f DirectiveNormalizerFunc) Normalize(dir *Directive) error {
return f(dir)
}
// DirectiveContext holds essential information about the field being resolved
// and the active HTTP request. Working as the context in a directive executor.
type DirectiveContext struct {
Directive
ValueType reflect.Type
Value reflect.Value
Request *http.Request
Context context.Context
resolver *fieldResolver
}
// DeliverContextValue binds a value to the specified key in the context. And it
// will be delivered among the executors in the same field resolver.
func (c *DirectiveContext) DeliverContextValue(key, value interface{}) {
c.Context = context.WithValue(c.Context, key, value)
}
func (c *DirectiveContext) decoderOf(t reflect.Type) interface{} {
return c.resolver.decoderOf(t)
}
// Directive defines the profile to locate an httpin.DirectiveExecutor instance
// and drive it with essential arguments.
type Directive struct {
Executor string // name of the executor
Argv []string // argv
}
// buildDirective builds a `directive` by parsing a directive string extracted
// from the struct tag.
//
// Example directives are:
// "form=page,page_index" -> { Executor: "form", Args: ["page", "page_index"] }
// "header=x-api-token" -> { Executor: "header", Args: ["x-api-token"] }
func buildDirective(directiveStr string) (*Directive, error) {
parts := strings.SplitN(directiveStr, "=", 2)
executor := parts[0]
var argv []string
if len(parts) == 2 {
// Split the remained string by delimiter `,` as argv.
argv = strings.Split(parts[1], ",")
}
// Ensure that the corresponding executor had been registered.
dir := &Directive{Executor: executor, Argv: argv}
if dir.getExecutor() == nil {
return nil, fmt.Errorf("%w: %q", ErrUnregisteredExecutor, dir.Executor)
}
// Normalize the directive.
norm := dir.getNormalizer()
if norm != nil {
if err := norm.Normalize(dir); err != nil {
return nil, fmt.Errorf("invalid directive %q: %w", dir.Executor, err)
}
}
return dir, nil
}
// Execute locates the executor and runs it with the specified context.
func (d *Directive) Execute(ctx *DirectiveContext) error {
return d.getExecutor().Execute(ctx)
}
// getExecutor locates the executor by its name. It must exist.
func (d *Directive) getExecutor() DirectiveExecutor {
return executors[d.Executor]
}
// getNormalizer locates the directive normalizer by its name.
func (d *Directive) getNormalizer() DirectiveNormalizer {
return normalizers[d.Executor]
}
func (d *Directive) isDecoderSpecifier() bool {
return d.Executor == "decoder"
}
// noop is a no-operation directive executor.
func noop(_ *DirectiveContext) error {
return nil
}