Remove old cli framework.

Also consolidate the leftover packages under cli.
Remove pkg/mflag.
Make manpage generation work with new cobra layout.
Remove remaining mflag and fix tests after rebase with master.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2016-06-23 11:25:51 -04:00
parent fc1a3d79f8
commit 38aca22dcd
4 changed files with 15 additions and 218 deletions

191
cli.go
View File

@ -1,191 +0,0 @@
package cli
import (
"errors"
"fmt"
"io"
"os"
"strings"
flag "github.com/docker/docker/pkg/mflag"
)
// Cli represents a command line interface.
type Cli struct {
Stderr io.Writer
handlers []Handler
Usage func()
}
// Handler holds the different commands Cli will call
// It should have methods with names starting with `Cmd` like:
// func (h myHandler) CmdFoo(args ...string) error
type Handler interface {
Command(name string) func(...string) error
}
// Initializer can be optionally implemented by a Handler to
// initialize before each call to one of its commands.
type Initializer interface {
Initialize() error
}
// New instantiates a ready-to-use Cli.
func New(handlers ...Handler) *Cli {
// make the generic Cli object the first cli handler
// in order to handle `docker help` appropriately
cli := new(Cli)
cli.handlers = append([]Handler{cli}, handlers...)
return cli
}
var errCommandNotFound = errors.New("command not found")
func (cli *Cli) command(args ...string) (func(...string) error, error) {
for _, c := range cli.handlers {
if c == nil {
continue
}
if cmd := c.Command(strings.Join(args, " ")); cmd != nil {
if ci, ok := c.(Initializer); ok {
if err := ci.Initialize(); err != nil {
return nil, err
}
}
return cmd, nil
}
}
return nil, errCommandNotFound
}
// Run executes the specified command.
func (cli *Cli) Run(args ...string) error {
if len(args) > 1 {
command, err := cli.command(args[:2]...)
if err == nil {
return command(args[2:]...)
}
if err != errCommandNotFound {
return err
}
}
if len(args) > 0 {
command, err := cli.command(args[0])
if err != nil {
if err == errCommandNotFound {
cli.noSuchCommand(args[0])
return nil
}
return err
}
return command(args[1:]...)
}
return cli.CmdHelp()
}
func (cli *Cli) noSuchCommand(command string) {
if cli.Stderr == nil {
cli.Stderr = os.Stderr
}
fmt.Fprintf(cli.Stderr, "docker: '%s' is not a docker command.\nSee 'docker --help'.\n", command)
os.Exit(1)
}
// Command returns a command handler, or nil if the command does not exist
func (cli *Cli) Command(name string) func(...string) error {
return map[string]func(...string) error{
"help": cli.CmdHelp,
}[name]
}
// CmdHelp displays information on a Docker command.
//
// If more than one command is specified, information is only shown for the first command.
//
// Usage: docker help COMMAND or docker COMMAND --help
func (cli *Cli) CmdHelp(args ...string) error {
if len(args) > 1 {
command, err := cli.command(args[:2]...)
if err == nil {
command("--help")
return nil
}
if err != errCommandNotFound {
return err
}
}
if len(args) > 0 {
command, err := cli.command(args[0])
if err != nil {
if err == errCommandNotFound {
cli.noSuchCommand(args[0])
return nil
}
return err
}
command("--help")
return nil
}
if cli.Usage == nil {
flag.Usage()
} else {
cli.Usage()
}
return nil
}
// Subcmd is a subcommand of the main "docker" command.
// A subcommand represents an action that can be performed
// from the Docker command line client.
//
// To see all available subcommands, run "docker --help".
func Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
var errorHandling flag.ErrorHandling
if exitOnError {
errorHandling = flag.ExitOnError
} else {
errorHandling = flag.ContinueOnError
}
flags := flag.NewFlagSet(name, errorHandling)
flags.Usage = func() {
flags.ShortUsage()
flags.PrintDefaults()
}
flags.ShortUsage = func() {
if len(synopses) == 0 {
synopses = []string{""}
}
// Allow for multiple command usage synopses.
for i, synopsis := range synopses {
lead := "\t"
if i == 0 {
// First line needs the word 'Usage'.
lead = "Usage:\t"
}
if synopsis != "" {
synopsis = " " + synopsis
}
fmt.Fprintf(flags.Out(), "\n%sdocker %s%s", lead, name, synopsis)
}
fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
}
return flags
}
// StatusError reports an unsuccessful exit by a command.
type StatusError struct {
Status string
StatusCode int
}
func (e StatusError) Error() string {
return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
}

View File

@ -1,4 +1,4 @@
package cobraadaptor package cli
import ( import (
"fmt" "fmt"
@ -17,12 +17,6 @@ func SetupRootCommand(rootCmd *cobra.Command) {
rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
} }
// GetRootCommand returns the root command. Required to generate the man pages
// and reference docs from a script outside this package.
func (c CobraAdaptor) GetRootCommand() *cobra.Command {
return c.rootCmd
}
// FlagErrorFunc prints an error messages which matches the format of the // FlagErrorFunc prints an error messages which matches the format of the
// docker/docker/cli error messages // docker/docker/cli error messages
func FlagErrorFunc(cmd *cobra.Command, err error) error { func FlagErrorFunc(cmd *cobra.Command, err error) error {

View File

@ -1,6 +1,9 @@
package cli package cli
import "strings" import (
"fmt"
"strings"
)
// Errors is a list of errors. // Errors is a list of errors.
// Useful in a loop if you don't want to return the error right away and you want to display after the loop, // Useful in a loop if you don't want to return the error right away and you want to display after the loop,
@ -18,3 +21,13 @@ func (errList Errors) Error() string {
} }
return strings.Join(out, ", ") return strings.Join(out, ", ")
} }
// StatusError reports an unsuccessful exit by a command.
type StatusError struct {
Status string
StatusCode int
}
func (e StatusError) Error() string {
return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
}

View File

@ -1,19 +0,0 @@
package cli
// Command is the struct containing the command name and description
type Command struct {
Name string
Description string
}
// DockerCommandUsage lists the top level docker commands and their short usage
var DockerCommandUsage = []Command{}
// DockerCommands stores all the docker command
var DockerCommands = make(map[string]Command)
func init() {
for _, cmd := range DockerCommandUsage {
DockerCommands[cmd.Name] = cmd
}
}