forked from motemen/go-cli
/
cli.go
161 lines (132 loc) · 3.74 KB
/
cli.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
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
// Package cli is a simple framework for creating CLI apps with commands.
// Its subpackage github.com/yuuki1/go-cli/gen provides a way to generate
// commands from document comments in the code.
package cli
import (
"bytes"
"flag"
"fmt"
"io"
"os"
"sort"
"text/tabwriter"
)
// App represents for a CLI program with commands.
type App struct {
Name string
Commands map[string]*Command
ErrorWriter io.Writer
FlagErrorHandling flag.ErrorHandling
}
// Command represents one of commands of App.
type Command struct {
Name string
// Short (one line) description of the command. Used when the program as
// invoked without a command name
Short string
// Long description of the command. The first line of Long should be a
// usage line i.e. it starts with the command name. Used when invoked with -h
Long string
// The actual implementation of the command. The function will receive two
// arguments, namely flags and args. flags is an flag.FlagSet and args are
// the command line arguments after the command name. flags is not
// initialized, so declaring flag variables and arguments parsing with
// flags.Parse(args) should be called inside the function.
//
// Return ErrUsage if you want to show user the command usage.
Action func(flags *flag.FlagSet, args []string) error
}
var (
// Default implementation of App. Its name is set to os.Args[0].
Default = &App{
Commands: Commands,
ErrorWriter: os.Stderr,
FlagErrorHandling: flag.ExitOnError,
}
// Commands is default value of Default.Commands.
Commands = map[string]*Command{}
)
// Run is a shortcut for Default.Run.
func Run(args []string) { Default.Run(args) }
// Use is a shortcut for Default.Use.
func Use(cmd *Command) { Default.Use(cmd) }
// ErrUsage is the error indicating the user had wrong usage.
var ErrUsage = fmt.Errorf("usage error")
var exit = os.Exit
func init() {
Default.Name = os.Args[0]
}
// Run is the entry point of the program. It recognizes the first element of
// args as a command name, and dispatches a command with rest arguments.
func (app *App) Run(args []string) {
if len(args) == 0 {
app.PrintUsage()
exit(2)
return
}
cmdName := args[0]
if cmd, ok := app.Commands[cmdName]; ok {
flags := flag.NewFlagSet(cmdName, app.FlagErrorHandling)
flags.Usage = func() {
fmt.Fprintln(app.ErrorWriter, cmd.Usage(flags))
}
err := cmd.Action(flags, args[1:])
if err != nil {
if err == ErrUsage {
flags.Usage()
exit(2)
return
} else if err == flag.ErrHelp {
exit(2)
return
} else {
fmt.Fprintln(app.ErrorWriter, err)
exit(1)
return
}
}
} else {
app.PrintUsage()
exit(2)
return
}
}
// PrintUsage prints out the usage of the program with its commands listed.
func (app *App) PrintUsage() {
fmt.Fprintf(app.ErrorWriter, "Usage: %s <command> [<args>]\n\n", app.Name)
fmt.Fprintf(app.ErrorWriter, "Commands:\n")
names := make([]string, 0, len(app.Commands))
for name := range app.Commands {
names = append(names, name)
}
sort.Strings(names)
w := tabwriter.NewWriter(app.ErrorWriter, 0, 8, 4, ' ', 0)
for _, name := range names {
fmt.Fprintf(w, " %s\t%s\n", name, app.Commands[name].Short)
}
w.Flush()
}
// Use registers a app command cmd.
func (app *App) Use(cmd *Command) {
app.Commands[cmd.Name] = cmd
}
// Usage returns a usage documentation of a command.
func (c Command) Usage(flags *flag.FlagSet) string {
usage := fmt.Sprintf("Usage: %s", c.Long)
if flags == nil {
return usage
}
var hasFlag bool
flags.VisitAll(func(_ *flag.Flag) {
hasFlag = true
})
if hasFlag == false {
return usage
}
buf := bytes.NewBufferString(usage)
buf.WriteString("\n\nOptions:\n")
defer flags.SetOutput(nil)
flags.SetOutput(buf)
flags.PrintDefaults()
return buf.String()
}