Skip to content
GitHub no longer supports this web browser. Learn more about the browsers we support.
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time. Cannot retrieve contributors at this time
142 lines (124 sloc) 3.27 KB
package main
import (
"errors"
"flag"
"fmt"
"log"
"os"
"strings"
)
// command is a subcommand handler and its flag set.
type command struct {
// flagSet is the flag set for the command.
flagSet *flag.FlagSet
// aliases for the command.
aliases []string
// handler is the function that is invoked to handle this command.
handler func(args []string) error
// flagSet.Usage function to invoke on e.g. -h flag. If nil, a default one
// one is used.
usageFunc func()
}
// matches tells if the given name matches this command or one of its aliases.
func (c *command) matches(name string) bool {
if name == c.flagSet.Name() {
return true
}
for _, alias := range c.aliases {
if name == alias {
return true
}
}
return false
}
// commander represents a top-level command with subcommands.
type commander []*command
// run runs the command.
func (c commander) run(flagSet *flag.FlagSet, cmdName, usageText string, args []string) {
// Parse flags.
flagSet.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), usageText)
}
if !flagSet.Parsed() {
flagSet.Parse(args)
}
// Print usage if the command is "help".
if flagSet.Arg(0) == "help" || flagSet.NArg() == 0 {
flagSet.Usage()
os.Exit(0)
}
// Configure default usage funcs for commands.
for _, cmd := range c {
cmd := cmd
if cmd.usageFunc != nil {
cmd.flagSet.Usage = cmd.usageFunc
continue
}
cmd.flagSet.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of '%s %s':\n", cmdName, cmd.flagSet.Name())
cmd.flagSet.PrintDefaults()
}
}
// Find the subcommand to execute.
name := flagSet.Arg(0)
for _, cmd := range c {
if !cmd.matches(name) {
continue
}
// Read global configuration now.
var err error
cfg, err = readConfig()
if err != nil {
log.Fatal("reading config: ", err)
}
// Parse subcommand flags.
args := flagSet.Args()[1:]
if err := cmd.flagSet.Parse(args); err != nil {
panic(fmt.Sprintf("all registered commands should use flag.ExitOnError: error: %s", err))
}
// Execute the subcommand.
if err := cmd.handler(flagSet.Args()[1:]); err != nil {
if _, ok := err.(*usageError); ok {
log.Println(err)
cmd.flagSet.Usage()
os.Exit(2)
}
if e, ok := err.(*exitCodeError); ok {
if e.error != nil {
log.Println(e.error)
}
os.Exit(e.exitCode)
}
log.Fatal(err)
}
os.Exit(0)
}
log.Printf("%s: unknown subcommand %q", cmdName, name)
log.Fatalf("Run '%s help' for usage.", cmdName)
}
// usageError is an error type that subcommands can return in order to signal
// that a usage error has occurred.
type usageError struct {
error
}
// exitCodeError is an error type that subcommands can return in order to
// specify the exact exit code.
type exitCodeError struct {
error
exitCode int
}
const (
graphqlErrorsExitCode = 2
)
func didYouMeanOtherCommand(actual string, suggested []string) *command {
fullSuggestions := make([]string, len(suggested))
for i, s := range suggested {
fullSuggestions[i] = "src " + s
}
msg := fmt.Sprintf("src: unknown subcommand %q\n\nDid you mean:\n\n\t%s", actual, strings.Join(fullSuggestions, "\n\t"))
return &command{
flagSet: flag.NewFlagSet(actual, flag.ExitOnError),
handler: func(args []string) error { return errors.New(msg) },
usageFunc: func() { log.Println(msg) },
}
}
You can’t perform that action at this time.