spf13--cobra/cobra.go

385 lines
9.1 KiB
Go
Raw Normal View History

2013-09-03 22:54:51 +00:00
// Copyright © 2013 Steve Francia <spf@spf13.com>.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Commands similar to git, go tools and other modern CLI tools
// inspired by go, go-Commander, gh and subcommand
package cobra
import (
"bytes"
"fmt"
flag "github.com/spf13/pflag"
"io"
2013-09-03 22:54:51 +00:00
"os"
"strings"
)
var _ = flag.ContinueOnError
// A Commander holds the configuration for the command line tool.
type Commander struct {
// A Commander is also a Command for top level and global help & flags
Command
args []string
output io.Writer // nil means stderr; use out() accessor
2013-09-03 22:54:51 +00:00
}
2013-09-11 13:20:59 +00:00
// Provide the user with a new commander.
2013-09-03 22:54:51 +00:00
func NewCommander() (c *Commander) {
c = new(Commander)
c.cmdr = c
2013-09-03 22:54:51 +00:00
return
}
2013-09-11 13:20:59 +00:00
// Name for commander, should match application name
2013-09-03 22:54:51 +00:00
func (c *Commander) SetName(name string) {
c.name = name
}
2013-09-11 13:20:59 +00:00
// os.Args[1:] by default, if desired, can be overridden
// particularly useful when testing.
2013-09-03 22:54:51 +00:00
func (c *Commander) SetArgs(a []string) {
c.args = a
}
2013-09-11 13:20:59 +00:00
// Call execute to use the args (os.Args[1:] by default)
// and run through the command tree finding appropriate matches
// for commands and then corresponding flags.
2013-09-04 15:23:13 +00:00
func (c *Commander) Execute() (err error) {
2013-09-03 22:54:51 +00:00
if len(c.args) == 0 {
2013-09-04 15:23:13 +00:00
err = c.execute(os.Args[1:])
2013-09-03 22:54:51 +00:00
} else {
2013-09-04 15:23:13 +00:00
err = c.execute(c.args)
2013-09-03 22:54:51 +00:00
}
2013-09-04 15:23:13 +00:00
return
2013-09-03 22:54:51 +00:00
}
func (c *Commander) out() io.Writer {
if c.output == nil {
return os.Stderr
}
return c.output
}
//Print to out
func (c *Commander) POut(i ...interface{}) {
fmt.Fprint(c.out(), i...)
}
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (c *Commander) SetOutput(output io.Writer) {
c.output = output
}
2013-09-03 22:54:51 +00:00
// Command is just that, a command for your application.
// eg. 'go run' ... 'run' is the command. Cobra requires
// you to define the usage and description as part of your command
// definition to ensure usability.
type Command struct {
// Name is the command name, usually the executable's name.
name string
// The one-line usage message.
Use string
// The short description shown in the 'help' output.
Short string
// The long message shown in the 'help <this-command>' output.
Long string
// Set of flags specific to this command.
flags *flag.FlagSet
// Set of flags children commands will inherit
pflags *flag.FlagSet
// Run runs the command.
// The args are the arguments after the command name.
Run func(cmd *Command, args []string)
// Commands is the list of commands supported by this Commander program.
commands []*Command
// Parent Command for this command
parent *Command
// Commander
cmdr *Commander
2013-09-03 22:54:51 +00:00
flagErrorBuf *bytes.Buffer
}
// find the target command given the args and command tree
func (c *Command) Find(args []string) (cmd *Command, a []string, err error) {
if c == nil {
return nil, nil, fmt.Errorf("Called find() on a nil Command")
}
validSubCommand := false
if len(args) > 1 && c.HasSubCommands() {
for _, cmd := range c.commands {
if cmd.Name() == args[0] {
validSubCommand = true
return cmd.Find(args[1:])
}
}
}
if !validSubCommand && c.Runnable() {
return c, args, nil
}
return nil, nil, nil
}
// execute the command determined by args and the command tree
func (c *Command) execute(args []string) (err error) {
err = fmt.Errorf("unknown subcommand %q\nRun 'help' for usage.\n", args[0])
if c == nil {
return fmt.Errorf("Called Execute() on a nil Command")
}
cmd, a, e := c.Find(args)
if e == nil {
err = cmd.ParseFlags(a)
2013-09-04 15:23:13 +00:00
if err != nil {
return err
} else {
argWoFlags := cmd.Flags().Args()
cmd.Run(cmd, argWoFlags)
return nil
}
2013-09-03 22:54:51 +00:00
}
err = e
return err
}
// Used for testing
func (c *Command) ResetCommands() {
c.commands = nil
}
2013-09-03 22:54:51 +00:00
// Add one or many commands as children of this
func (c *Command) AddCommand(cmds ...*Command) {
for i, x := range cmds {
if cmds[i] == c {
panic("Command can't be a child of itself")
}
2013-09-03 22:54:51 +00:00
cmds[i].parent = c
cmds[i].cmdr = cmds[i].parent.cmdr
2013-09-03 22:54:51 +00:00
c.commands = append(c.commands, x)
}
}
func (c *Command) Print(i ...interface{}) {
c.cmdr.POut(i...)
}
func (c *Command) Println(i ...interface{}) {
str := fmt.Sprintln(i...)
c.Print(str)
}
func (c *Command) Printf(format string, i ...interface{}) {
str := fmt.Sprintf(format, i...)
c.Print(str)
}
2013-09-03 22:54:51 +00:00
// The full usage for a given command (including parents)
func (c *Command) Usage(depth ...int) string {
i := 0
if len(depth) > 0 {
i = depth[0]
}
if c.HasParent() {
return c.parent.Usage(i+1) + " " + c.Use
} else if i > 0 {
return c.Name()
} else {
return c.Use
}
}
2013-09-11 13:20:59 +00:00
// For use in determining which flags have been assigned to which commands
// and which persist
func (c *Command) DebugFlags() {
c.Println("DebugFlags called on", c.Name())
2013-09-10 22:27:31 +00:00
var debugflags func(*Command)
debugflags = func(x *Command) {
if x.HasFlags() || x.HasPersistentFlags() {
c.Println(x.Name())
2013-09-10 22:27:31 +00:00
}
if x.HasFlags() {
x.flags.VisitAll(func(f *flag.Flag) {
if x.HasPersistentFlags() {
if x.persistentFlag(f.Name) == nil {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [L]")
2013-09-10 22:27:31 +00:00
} else {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [LP]")
2013-09-10 22:27:31 +00:00
}
} else {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [L]")
2013-09-10 22:27:31 +00:00
}
})
}
if x.HasPersistentFlags() {
x.pflags.VisitAll(func(f *flag.Flag) {
if x.HasFlags() {
if x.flags.Lookup(f.Name) == nil {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [P]")
2013-09-10 22:27:31 +00:00
}
} else {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [P]")
2013-09-10 22:27:31 +00:00
}
})
}
c.Println(x.flagErrorBuf)
2013-09-10 22:27:31 +00:00
if x.HasSubCommands() {
for _, y := range x.commands {
debugflags(y)
}
}
}
2013-09-10 22:27:31 +00:00
debugflags(c)
}
2013-09-03 22:54:51 +00:00
// Usage prints the usage details to the standard output.
func (c *Command) PrintUsage() {
if c.Runnable() {
c.Printf("usage: %s\n\n", c.Usage())
2013-09-03 22:54:51 +00:00
}
c.Println(strings.Trim(c.Long, "\n"))
2013-09-03 22:54:51 +00:00
}
// Name returns the command's name: the first word in the use line.
func (c *Command) Name() string {
if c.name != "" {
return c.name
}
name := c.Use
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
// Determine if the command is itself runnable
func (c *Command) Runnable() bool {
return c.Run != nil
}
// Determine if the command has children commands
func (c *Command) HasSubCommands() bool {
return len(c.commands) > 0
}
2013-09-11 13:20:59 +00:00
// Determine if the command is a child command
2013-09-03 22:54:51 +00:00
func (c *Command) HasParent() bool {
return c.parent != nil
}
// Get the Commands FlagSet
func (c *Command) Flags() *flag.FlagSet {
if c.flags == nil {
c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
2013-09-03 22:54:51 +00:00
c.flags.SetOutput(c.flagErrorBuf)
}
return c.flags
}
// Get the Commands Persistent FlagSet
func (c *Command) PersistentFlags() *flag.FlagSet {
if c.pflags == nil {
c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
2013-09-03 22:54:51 +00:00
c.pflags.SetOutput(c.flagErrorBuf)
}
return c.pflags
2013-09-03 22:54:51 +00:00
}
// Intended for use in testing
func (c *Command) ResetFlags() {
c.flagErrorBuf = new(bytes.Buffer)
2013-09-03 22:54:51 +00:00
c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.flags.SetOutput(c.flagErrorBuf)
c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.pflags.SetOutput(c.flagErrorBuf)
}
2013-09-11 13:20:59 +00:00
// Does the command contain flags (local not persistent)
2013-09-03 22:54:51 +00:00
func (c *Command) HasFlags() bool {
return c.Flags().HasFlags()
2013-09-03 22:54:51 +00:00
}
2013-09-11 13:20:59 +00:00
// Does the command contain persistent flags
2013-09-03 22:54:51 +00:00
func (c *Command) HasPersistentFlags() bool {
return c.PersistentFlags().HasFlags()
2013-09-03 22:54:51 +00:00
}
// Climbs up the command tree looking for matching flag
func (c *Command) Flag(name string) (flag *flag.Flag) {
flag = c.Flags().Lookup(name)
if flag == nil {
flag = c.persistentFlag(name)
}
return
}
// recursively find matching persistent flag
func (c *Command) persistentFlag(name string) (flag *flag.Flag) {
if c.HasPersistentFlags() {
flag = c.PersistentFlags().Lookup(name)
}
if flag == nil && c.HasParent() {
flag = c.parent.persistentFlag(name)
}
return
}
// Parses persistent flag tree & local flags
func (c *Command) ParseFlags(args []string) (err error) {
c.mergePersistentFlags()
2013-09-03 22:54:51 +00:00
err = c.Flags().Parse(args)
if err != nil {
return err
}
return nil
}
func (c *Command) mergePersistentFlags() {
var rmerge func(x *Command)
rmerge = func(x *Command) {
if x.HasPersistentFlags() {
x.PersistentFlags().VisitAll(func(f *flag.Flag) {
if c.Flags().Lookup(f.Name) == nil {
c.Flags().AddFlag(f)
}
})
}
if x.HasParent() {
rmerge(x.parent)
}
}
rmerge(c)
}