hub/commands/args.go

252 строки
4.7 KiB
Go

package commands
import (
"fmt"
"strings"
"github.com/github/hub/cmd"
"github.com/github/hub/utils"
)
type Args struct {
Executable string
GlobalFlags []string
Command string
ProgramPath string
Params []string
beforeChain []*cmd.Cmd
afterChain []*cmd.Cmd
Noop bool
Terminator bool
noForward bool
Callbacks []func() error
Flag *utils.ArgsParser
}
func (a *Args) Words() []string {
aa := make([]string, 0)
for _, p := range a.Params {
if !looksLikeFlag(p) {
aa = append(aa, p)
}
}
return aa
}
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))
}
func (a *Args) AfterFn(fn func() error) {
a.Callbacks = append(a.Callbacks, fn)
}
func (a *Args) NoForward() {
a.noForward = true
}
func (a *Args) Replace(executable, command string, params ...string) {
a.Executable = executable
a.Command = command
a.Params = params
a.GlobalFlags = []string{}
a.noForward = false
}
func (a *Args) Commands() []*cmd.Cmd {
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)
}
}
for _, c := range a.beforeChain {
appendFromChain(c)
}
if !a.noForward {
result = append(result, a.ToCmd())
}
for _, c := range a.afterChain {
appendFromChain(c)
}
return result
}
func (a *Args) ToCmd() *cmd.Cmd {
c := cmd.NewWithArray(append([]string{a.Executable}, a.GlobalFlags...))
if a.Command != "" {
c.WithArg(a.Command)
}
for _, arg := range a.Params {
if arg != "" {
c.WithArg(arg)
}
}
return c
}
func (a *Args) GetParam(i int) string {
return a.Params[i]
}
func (a *Args) FirstParam() string {
if a.ParamsSize() == 0 {
panic(fmt.Sprintf("Index 0 is out of bound"))
}
return a.Params[0]
}
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]
}
func (a *Args) HasSubcommand() bool {
return !a.IsParamsEmpty() && a.Params[0][0] != '-'
}
func (a *Args) InsertParam(i int, items ...string) {
if i < 0 {
panic(fmt.Sprintf("Index %d is out of bound", i))
}
if i > a.ParamsSize() {
i = a.ParamsSize()
}
newParams := make([]string, 0)
newParams = append(newParams, a.Params[:i]...)
newParams = append(newParams, items...)
newParams = append(newParams, a.Params[i:]...)
a.Params = newParams
}
func (a *Args) RemoveParam(i int) string {
item := a.Params[i]
a.Params = append(a.Params[:i], a.Params[i+1:]...)
return item
}
func (a *Args) ReplaceParam(i int, item string) {
if i < 0 || i > a.ParamsSize()-1 {
panic(fmt.Sprintf("Index %d is out of bound", i))
}
a.Params[i] = item
}
func (a *Args) IndexOfParam(param string) int {
for i, p := range a.Params {
if p == param {
return i
}
}
return -1
}
func (a *Args) ParamsSize() int {
return len(a.Params)
}
func (a *Args) IsParamsEmpty() bool {
return a.ParamsSize() == 0
}
func (a *Args) PrependParams(params ...string) {
a.Params = append(params, a.Params...)
}
func (a *Args) AppendParams(params ...string) {
a.Params = append(a.Params, params...)
}
func NewArgs(args []string) *Args {
var (
command string
params []string
noop bool
)
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:]...)
}
}
}
if len(args) != 0 {
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),
}
}
const (
noopFlag = "--noop"
versionFlag = "--version"
listCmds = "--list-cmds="
helpFlag = "--help"
configFlag = "-c"
chdirFlag = "-C"
flagPrefix = "-"
)
func looksLikeFlag(value string) bool {
return strings.HasPrefix(value, flagPrefix)
}
func findCommandIndex(args []string) int {
slurpNextValue := false
commandIndex := 0
for i, arg := range args {
if slurpNextValue {
commandIndex = i + 1
slurpNextValue = false
} else if arg == versionFlag || arg == helpFlag || strings.HasPrefix(arg, listCmds) || !looksLikeFlag(arg) {
break
} else {
commandIndex = i + 1
if arg == configFlag || arg == chdirFlag {
slurpNextValue = true
}
}
}
return commandIndex
}