hub/commands/commands.go

163 строки
3.5 KiB
Go

package commands
import (
"fmt"
"regexp"
"strings"
"github.com/github/hub/v2/utils"
)
var (
NameRe = `[\w.-]+`
OwnerRe = "[a-zA-Z0-9][a-zA-Z0-9-]*"
NameWithOwnerRe = fmt.Sprintf(`^(%s/)?%s$`, OwnerRe, NameRe)
CmdRunner = NewRunner()
)
type Command struct {
Run func(cmd *Command, args *Args)
Key string
Usage string
Long string
KnownFlags string
GitExtension bool
subCommands map[string]*Command
parentCommand *Command
}
func (c *Command) Call(args *Args) (err error) {
runCommand, err := c.lookupSubCommand(args)
if err != nil {
return
}
if !c.GitExtension {
err = runCommand.parseArguments(args)
if err != nil {
return
}
}
runCommand.Run(runCommand, args)
return
}
type ErrHelp struct {
err string
}
func (e ErrHelp) Error() string {
return e.err
}
func (c *Command) parseArguments(args *Args) error {
knownFlags := c.KnownFlags
if knownFlags == "" {
knownFlags = c.Long
}
args.Flag = utils.NewArgsParserWithUsage("-h, --help\n" + knownFlags)
rest, err := args.Flag.Parse(args.Params)
if err != nil {
return fmt.Errorf("%s\n%s", err, c.Synopsis())
}
if args.Flag.Bool("--help") {
return &ErrHelp{err: c.Synopsis()}
}
args.Params = rest
args.Terminator = args.Flag.HasTerminated
return nil
}
func (c *Command) Use(subCommand *Command) {
if c.subCommands == nil {
c.subCommands = make(map[string]*Command)
}
c.subCommands[subCommand.Name()] = subCommand
subCommand.parentCommand = c
}
func (c *Command) UsageError(msg string) error {
nl := ""
if msg != "" {
nl = "\n"
}
return fmt.Errorf("%s%s%s", msg, nl, c.Synopsis())
}
func (c *Command) Synopsis() string {
lines := []string{}
usagePrefix := "Usage:"
usageStr := c.Usage
if usageStr == "" && c.parentCommand != nil {
usageStr = c.parentCommand.Usage
}
for _, line := range strings.Split(usageStr, "\n") {
if line != "" {
usage := fmt.Sprintf("%s hub %s", usagePrefix, line)
usagePrefix = " "
lines = append(lines, usage)
}
}
return strings.Join(lines, "\n")
}
func (c *Command) HelpText() string {
usage := strings.Replace(c.Usage, "-^", "`-^`", 1)
usageRe := regexp.MustCompile(`(?m)^([a-z-]+)(.*)$`)
usage = usageRe.ReplaceAllString(usage, "`hub $1`$2 ")
usage = strings.TrimSpace(usage)
var desc string
long := strings.TrimSpace(c.Long)
if lines := strings.Split(long, "\n"); len(lines) > 1 {
desc = lines[0]
long = strings.Join(lines[1:], "\n")
}
long = strings.Replace(long, "''", "`", -1)
headingRe := regexp.MustCompile(`(?m)^(## .+):$`)
long = headingRe.ReplaceAllString(long, "$1")
indentRe := regexp.MustCompile(`(?m)^\t`)
long = indentRe.ReplaceAllLiteralString(long, "")
definitionListRe := regexp.MustCompile(`(?m)^(\* )?([^#\s][^\n]*?):?\n\t`)
long = definitionListRe.ReplaceAllString(long, "$2\n:\t")
return fmt.Sprintf("hub-%s(1) -- %s\n===\n\n## Synopsis\n\n%s\n%s", c.Name(), desc, usage, long)
}
func (c *Command) Name() string {
if c.Key != "" {
return c.Key
}
usageLine := strings.Split(strings.TrimSpace(c.Usage), "\n")[0]
return strings.Split(usageLine, " ")[0]
}
func (c *Command) Runnable() bool {
return c.Run != nil
}
func (c *Command) lookupSubCommand(args *Args) (runCommand *Command, err error) {
if len(c.subCommands) > 0 && args.HasSubcommand() {
subCommandName := args.FirstParam()
if subCommand, ok := c.subCommands[subCommandName]; ok {
runCommand = subCommand
args.Params = args.Params[1:]
} else {
err = fmt.Errorf("error: Unknown subcommand: %s", subCommandName)
}
} else {
runCommand = c
}
return
}