-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcommands.go
113 lines (98 loc) · 3.05 KB
/
commands.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
package configstruct
import (
"flag"
"fmt"
"strings"
)
// CommandFunc is a function that is executed when a command is referenced in a CLI call
type CommandFunc func(c *Command, cfg interface{}) error
// Command defines a command that consists of a name (empty for root command), a struct that models all
// flags, a function that is executed if the command matches and that gets the config struct as argument
// several sub-commands can be added
type Command struct {
fs *flag.FlagSet
config interface{}
f CommandFunc
subCommands []*Command
rootCommand *Command
dependencies map[string]interface{}
}
// NewCommand creates a command that is triggered by the given name in the command line
// all flags are defined by a struct that is parsed and filled with real values
// this struct is then set as argument for the function that is executed if the name matches
func NewCommand(name string, description string, config interface{}, f CommandFunc, subCommands ...*Command) *Command {
fs := flag.NewFlagSet(name, flag.ExitOnError)
fs.Usage = func() {
fmt.Fprint(fs.Output(), description+"\n\n")
if name == "" {
fmt.Fprintf(fs.Output(), "Usage:\n")
} else {
fmt.Fprintf(fs.Output(), "Usage of %s:\n", name)
}
fs.PrintDefaults()
if len(subCommands) > 0 {
fmt.Fprintf(fs.Output(), "\nAvailable Commands:\n")
for i := range subCommands {
fmt.Fprintf(fs.Output(), subCommands[i].fs.Name()+" ")
}
fmt.Fprintf(fs.Output(), "\n")
}
}
return &Command{
fs: fs,
config: config,
f: f,
subCommands: subCommands,
dependencies: make(map[string]interface{}),
}
}
// ParseAndRun parses the given arguments and executes command functions
func (c *Command) ParseAndRun(args []string, opts ...Option) error {
err := ParseWithFlagSet(c.fs, args, c.config, opts...)
if err != nil {
return err
}
if c.f != nil && (c.fs.Name() == "" || strings.EqualFold(c.fs.Name(), args[0])) {
err := c.f(c, c.config)
if err != nil {
return err
}
}
args = c.fs.Args()
if len(c.subCommands) > 0 && len(args) == 0 {
c.fs.Usage()
return nil
}
if len(args) > 0 {
cmdFound := false
for i := range c.subCommands {
if strings.EqualFold(c.subCommands[i].fs.Name(), args[0]) {
c.subCommands[i].rootCommand = c
cmdFound = true
if err := c.subCommands[i].ParseAndRun(args); err != nil {
return err
}
}
}
if !cmdFound {
fmt.Fprintf(c.fs.Output(), "Command '%s' not defined\n\n", args[0])
c.fs.Usage()
}
}
return nil
}
// SetDependency saves a dependency referenced by a name for subcommands
func (c *Command) SetDependency(name string, dep interface{}) {
c.dependencies[name] = dep
}
// GetDependency gets a previous saved dependency in this command or any of the parent commands in the chain
func (c *Command) GetDependency(name string) (interface{}, error) {
depValue, found := c.dependencies[name]
if found {
return depValue, nil
}
if c.rootCommand == nil {
return nil, fmt.Errorf("dependency %s not found", name)
}
return c.rootCommand.GetDependency(name)
}