args.go 4.7 KB
Newer Older
J
Jingwen Owen Ou 已提交
1 2
package commands

J
Jingwen Owen Ou 已提交
3 4
import (
	"fmt"
5
	"strings"
6 7

	"github.com/github/hub/cmd"
8
	"github.com/github/hub/utils"
J
Jingwen Owen Ou 已提交
9 10
)

J
Jingwen Owen Ou 已提交
11
type Args struct {
J
Jingwen Owen Ou 已提交
12
	Executable  string
13
	GlobalFlags []string
J
Jingwen Owen Ou 已提交
14
	Command     string
15
	ProgramPath string
J
Jingwen Owen Ou 已提交
16
	Params      []string
17 18
	beforeChain []*cmd.Cmd
	afterChain  []*cmd.Cmd
J
Jingwen Owen Ou 已提交
19
	Noop        bool
20
	Terminator  bool
21 22
	noForward   bool
	Callbacks   []func() error
23
	Flag        *utils.ArgsParser
24 25
}

26 27 28
func (a *Args) Words() []string {
	aa := make([]string, 0)
	for _, p := range a.Params {
29
		if !looksLikeFlag(p) {
30 31 32 33 34 35 36
			aa = append(aa, p)
		}
	}

	return aa
}

37 38 39 40 41 42 43 44
func (a *Args) Before(command ...string) {
	a.beforeChain = append(a.beforeChain, cmd.NewWithArray(command))
}

func (a *Args) After(command ...string) {
	a.afterChain = append(a.afterChain, cmd.NewWithArray(command))
}

45 46 47 48 49 50 51 52
func (a *Args) AfterFn(fn func() error) {
	a.Callbacks = append(a.Callbacks, fn)
}

func (a *Args) NoForward() {
	a.noForward = true
}

53 54 55 56
func (a *Args) Replace(executable, command string, params ...string) {
	a.Executable = executable
	a.Command = command
	a.Params = params
57
	a.GlobalFlags = []string{}
58
	a.noForward = false
59 60
}

61
func (a *Args) Commands() []*cmd.Cmd {
62 63 64 65 66 67 68 69 70 71 72
	result := []*cmd.Cmd{}
	appendFromChain := func(c *cmd.Cmd) {
		if c.Name == "git" {
			ga := []string{c.Name}
			ga = append(ga, a.GlobalFlags...)
			ga = append(ga, c.Args...)
			result = append(result, cmd.NewWithArray(ga))
		} else {
			result = append(result, c)
		}
	}
73

74 75 76
	for _, c := range a.beforeChain {
		appendFromChain(c)
	}
77 78 79
	if !a.noForward {
		result = append(result, a.ToCmd())
	}
80 81 82
	for _, c := range a.afterChain {
		appendFromChain(c)
	}
83 84 85 86 87

	return result
}

func (a *Args) ToCmd() *cmd.Cmd {
88
	c := cmd.New(a.Executable)
89
	c.WithArgs(a.GlobalFlags...)
90 91

	if a.Command != "" {
92
		c.WithArg(a.Command)
93 94 95 96
	}

	for _, arg := range a.Params {
		if arg != "" {
97
			c.WithArg(arg)
98 99 100
		}
	}

101
	return c
J
Jingwen Owen Ou 已提交
102 103
}

J
Jingwen Owen Ou 已提交
104 105
func (a *Args) GetParam(i int) string {
	return a.Params[i]
J
Jingwen Owen Ou 已提交
106 107
}

J
Jingwen Owen Ou 已提交
108 109 110 111
func (a *Args) FirstParam() string {
	if a.ParamsSize() == 0 {
		panic(fmt.Sprintf("Index 0 is out of bound"))
	}
J
Jingwen Owen Ou 已提交
112

J
Jingwen Owen Ou 已提交
113
	return a.Params[0]
J
Jingwen Owen Ou 已提交
114 115
}

J
Jingwen Owen Ou 已提交
116 117 118 119 120 121
func (a *Args) LastParam() string {
	if a.ParamsSize()-1 < 0 {
		panic(fmt.Sprintf("Index %d is out of bound", a.ParamsSize()-1))
	}

	return a.Params[a.ParamsSize()-1]
J
Jingwen Owen Ou 已提交
122 123
}

124 125 126 127
func (a *Args) HasSubcommand() bool {
	return !a.IsParamsEmpty() && a.Params[0][0] != '-'
}

J
Jingwen Owen Ou 已提交
128
func (a *Args) InsertParam(i int, items ...string) {
129
	if i < 0 {
J
Jingwen Owen Ou 已提交
130 131 132
		panic(fmt.Sprintf("Index %d is out of bound", i))
	}

133 134 135 136 137
	if i > a.ParamsSize() {
		i = a.ParamsSize()
	}

	newParams := make([]string, 0)
J
Jingwen Owen Ou 已提交
138 139 140 141 142 143 144
	newParams = append(newParams, a.Params[:i]...)
	newParams = append(newParams, items...)
	newParams = append(newParams, a.Params[i:]...)

	a.Params = newParams
}

J
Jingwen Owen Ou 已提交
145
func (a *Args) RemoveParam(i int) string {
146 147
	item := a.Params[i]
	a.Params = append(a.Params[:i], a.Params[i+1:]...)
J
Jingwen Owen Ou 已提交
148 149 150
	return item
}

J
Jingwen Owen Ou 已提交
151
func (a *Args) ReplaceParam(i int, item string) {
J
Jingwen Owen Ou 已提交
152
	if i < 0 || i > a.ParamsSize()-1 {
J
Jingwen Owen Ou 已提交
153 154 155
		panic(fmt.Sprintf("Index %d is out of bound", i))
	}

J
Jingwen Owen Ou 已提交
156
	a.Params[i] = item
J
Jingwen Owen Ou 已提交
157 158
}

J
Jingwen Owen Ou 已提交
159 160 161
func (a *Args) IndexOfParam(param string) int {
	for i, p := range a.Params {
		if p == param {
J
Jingwen Owen Ou 已提交
162 163 164 165 166 167 168
			return i
		}
	}

	return -1
}

J
Jingwen Owen Ou 已提交
169 170
func (a *Args) ParamsSize() int {
	return len(a.Params)
J
Jingwen Owen Ou 已提交
171 172
}

J
Jingwen Owen Ou 已提交
173 174
func (a *Args) IsParamsEmpty() bool {
	return a.ParamsSize() == 0
J
Jingwen Owen Ou 已提交
175 176
}

J
Jingwen Owen Ou 已提交
177 178 179 180
func (a *Args) PrependParams(params ...string) {
	a.Params = append(params, a.Params...)
}

J
Jingwen Owen Ou 已提交
181 182
func (a *Args) AppendParams(params ...string) {
	a.Params = append(a.Params, params...)
183 184
}

J
Jingwen Owen Ou 已提交
185
func NewArgs(args []string) *Args {
186
	var (
187 188 189
		command string
		params  []string
		noop    bool
J
Jingwen Owen Ou 已提交
190 191
	)

192 193 194 195 196 197 198 199 200 201 202
	cmdIdx := findCommandIndex(args)
	globalFlags := args[:cmdIdx]
	if cmdIdx > 0 {
		args = args[cmdIdx:]
		for i := len(globalFlags) - 1; i >= 0; i-- {
			if globalFlags[i] == noopFlag {
				noop = true
				globalFlags = append(globalFlags[:i], globalFlags[i+1:]...)
			}
		}
	}
J
Jingwen Owen Ou 已提交
203

204
	if len(args) != 0 {
J
Jingwen Owen Ou 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
		command = args[0]
		params = args[1:]
	}

	return &Args{
		Executable:  "git",
		GlobalFlags: globalFlags,
		Command:     command,
		Params:      params,
		Noop:        noop,
		beforeChain: make([]*cmd.Cmd, 0),
		afterChain:  make([]*cmd.Cmd, 0),
	}
}

220
const (
221
	noopFlag    = "--noop"
222
	versionFlag = "--version"
223
	listCmds    = "--list-cmds="
224 225 226
	helpFlag    = "--help"
	configFlag  = "-c"
	chdirFlag   = "-C"
227
	flagPrefix  = "-"
228 229
)

230 231 232 233
func looksLikeFlag(value string) bool {
	return strings.HasPrefix(value, flagPrefix)
}

234
func findCommandIndex(args []string) int {
235 236 237
	slurpNextValue := false
	commandIndex := 0

238
	for i, arg := range args {
239 240 241
		if slurpNextValue {
			commandIndex = i + 1
			slurpNextValue = false
242
		} else if arg == versionFlag || arg == helpFlag || strings.HasPrefix(arg, listCmds) || !looksLikeFlag(arg) {
243 244 245
			break
		} else {
			commandIndex = i + 1
246
			if arg == configFlag || arg == chdirFlag {
247 248 249
				slurpNextValue = true
			}
		}
250
	}
251
	return commandIndex
252
}