-
Notifications
You must be signed in to change notification settings - Fork 0
/
check.go
125 lines (106 loc) · 2.44 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
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 cli
import (
"fmt"
"strings"
)
// ErrFatal is used to signal fatal errors coming from `Fatal`.
type ErrFatal struct {
error
}
// ErrUsage is used to signal fatal errors caused by invalid commandline usage.
type ErrUsage struct {
error
}
// Fatal panics with an instance of ErrFatal with a formatted message.
func Fatal(msg string, args ...interface{}) {
panic(ErrFatal{fmt.Errorf(msg, args...)})
}
// Check panics with an instance of ErrFatal if err != nil.
func Check(err error) {
if err != nil {
panic(ErrFatal{err})
}
}
// Run runs the Spec with the given args. The loader is used to load
// option values from multiple sources (flags, env, yaml, etc).
// Panics of type ErrFatal and ErrUsage are recovered and returned as an error,
// all other panics are passed through.
func Run(spec Spec, l *Loader, raw []string) (err error) {
defer func() {
if r := recover(); r != nil {
switch z := r.(type) {
case ErrUsage:
err = z
return
case ErrFatal:
err = z
return
default:
panic(r)
}
}
}()
cmd := spec.Cmd()
err = validateArgs(cmd.Args, raw)
if err != nil {
return err
}
err = loadArgs(cmd.Args, l, raw)
if err != nil {
return
}
// load option values.
l.Load()
errs := l.Errors()
if errs != nil {
return combineErrors(errs)
}
spec.Run()
return
}
func combineErrors(errs []error) error {
var lines []string
for _, err := range errs {
lines = append(lines, err.Error())
}
return fmt.Errorf(strings.Join(lines, "\n"))
}
func loadArgs(args []*Arg, l *Loader, raw []string) error {
for i := 0; i < len(args); i++ {
arg := args[i]
var val interface{}
if arg.Variadic {
val = raw[i:]
} else {
val = raw[i]
}
err := l.Coerce(arg.Value, val)
if err != nil {
return ErrUsage{err}
}
}
return nil
}
// validateArgs checks that the number of args given on the CLI
// matches the number of args needed by the CLI function.
func validateArgs(specs []*Arg, args []string) error {
if len(specs) == 0 && len(args) > 0 {
return ErrUsage{fmt.Errorf("unexpected args %v", args)}
}
if len(specs) == 0 {
return nil
}
// variadic functions e.g. func HelloWorld(names ...string)
variadic := specs[len(specs)-1].Variadic
if variadic {
min := len(specs) - 1
if len(args) < min {
return ErrUsage{fmt.Errorf("expected at least %d arg", min)}
}
return nil
}
if len(args) != len(specs) {
return ErrUsage{fmt.Errorf("expected exactly %d args", len(specs))}
}
return nil
}