-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck.go
78 lines (66 loc) · 2.03 KB
/
check.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
package subcmd
import (
"reflect"
"github.com/pkg/errors"
)
// Check checks that the type of subcmd.F matches the expectations set by subcmd.Params:
//
// - It must be a function;
// - It must return no more than one value;
// - If it returns a value, that value must be of type error;
// - It must take an initial context.Context parameter;
// - It must take a final []string or ...string parameter;
// - The length of subcmd.Params must match the number of parameters subcmd.F takes (not counting the initial context.Context and final []string parameters);
// - Each parameter in subcmd.Params must match the corresponding parameter in subcmd.F.
//
// It also checks that the default value of each parameter in subcmd.Params matches the parameter's type.
func Check(subcmd Subcmd) error {
fv := reflect.ValueOf(subcmd.F)
ft := fv.Type()
if err := checkFuncType(ft, subcmd.Params); err != nil {
return err
}
for i, param := range subcmd.Params {
if err := checkParam(param); err != nil {
return errors.Wrapf(err, "checking parameter %d", i+1)
}
}
return nil
}
func checkFuncType(ft reflect.Type, params []Param) error {
in := make([]reflect.Type, 0, 2+len(params))
in = append(in, ctxType)
for _, param := range params {
in = append(in, param.Type.reflectType())
}
in = append(in, strSliceType)
out := []reflect.Type{errType}
if ft == reflect.FuncOf(in, nil, true) {
return nil
}
if ft == reflect.FuncOf(in, out, true) {
return nil
}
if ft == reflect.FuncOf(in, nil, false) {
return nil
}
if want := reflect.FuncOf(in, out, false); ft != want {
return FuncTypeErr{Got: ft, Want: want}
}
return nil
}
func checkParam(param Param) error {
if !reflect.TypeOf(param.Default).AssignableTo(param.Type.reflectType()) {
return ParamDefaultErr{Param: param}
}
return nil
}
// CheckMap calls [Check] on each of the entries in the Map.
func CheckMap(m Map) error {
for name, subcmd := range m {
if err := Check(subcmd); err != nil {
return errors.Wrapf(err, "checking subcommand %s", name)
}
}
return nil
}