-
-
Notifications
You must be signed in to change notification settings - Fork 366
/
command.go
112 lines (92 loc) · 2.07 KB
/
command.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
package cli
import (
"flag"
"io"
"os"
"path/filepath"
"strings"
"github.com/maxence-charriere/go-app/v7/pkg/errors"
)
var (
errNoRootCmd = errors.New("no root command")
)
type command struct {
help string
name string
options interface{}
}
func (c *command) Help(h string) Command {
c.help = h
return c
}
func (c *command) Options(o interface{}) Command {
c.options = o
return c
}
type commandManager struct {
out io.Writer
commands map[string]Command
}
func (m *commandManager) register(cmd ...string) Command {
k := commandString(cmd...)
c := &command{name: k}
if m.commands == nil {
m.commands = make(map[string]Command)
}
m.commands[k] = c
return c
}
func (m *commandManager) parse(args ...string) (string, func(), error) {
cmdslice, optsSlice := splitCommand(args)
k := commandString(cmdslice...)
i, ok := m.commands[k]
if !ok {
err := errNoRootCmd
if k != "" {
err = errors.New("unknown command").Tag("command-name", k)
}
return "", commandUsageIndex(m.out, m.commands), err
}
cmd := i.(*command)
programName := filepath.Base(os.Args[0])
flags := flag.NewFlagSet(commandString(programName, k), flag.ContinueOnError)
flags.SetOutput(writerNoop{})
optsParser := optionParser{flags: flags}
opts, err := optsParser.parse(cmd.options)
if err != nil {
return "", nil, errors.New("parsing options failed").
Tag("command-name", k).
Wrap(err)
}
usage := commandUsage(m.out, cmd, opts)
flags.Usage = func() {}
return cmd.name, usage, flags.Parse(optsSlice)
}
func commandString(cmd ...string) string {
clean := make([]string, 0, len(cmd))
for _, c := range cmd {
if c == "" {
continue
}
clean = append(clean, strings.TrimSpace(c))
}
return strings.Join(clean, " ")
}
func splitCommand(args []string) (cmd, opts []string) {
end := commandEndIndex(args)
return args[:end], args[end:]
}
func commandEndIndex(args []string) int {
i := 0
for i < len(args) {
if strings.HasPrefix(args[i], "-") {
return i
}
i++
}
return i
}
type writerNoop struct{}
func (w writerNoop) Write([]byte) (int, error) {
return 0, nil
}