func (cmd *Command) Path() string {
	return cmd.pathWithSep(" ")
}
Path returns space-separated command full name
package cli
import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"github.com/labstack/gommon/color"
	"github.com/mattn/go-colorable"
)
var commandNameRegexp = regexp.MustCompile("^[a-zA-Z_0-9][a-zA-Z_\\-0-9]*$")
func IsValidCommandName(commandName string) bool {
	return commandNameRegexp.MatchString(commandName)
}
type (
	CommandFunc func(*Context) error
	ArgvFunc func() interface{}
	NumCheckFunc func(n int) bool
	UsageFunc func() string
)
func ExactN(num int) NumCheckFunc { return func(n int) bool { return n == num } }
func AtLeast(num int) NumCheckFunc { return func(n int) bool { return n >= num } }
func AtMost(num int) NumCheckFunc { return func(n int) bool { return n <= num } }
type (
	Command struct {
		Name    string    
		Aliases []string  
		Desc    string    
		Text    string    
		CanSubRoute bool
		NoHook bool
		Global bool
		Fn        CommandFunc   
		UsageFn   UsageFunc     
		Argv      ArgvFunc      
		NumArg    NumCheckFunc  
		NumOption NumCheckFunc  
		HTTPRouters []string
		HTTPMethods []string
		OnBefore func(*Context) error
		OnAfter  func(*Context) error
		OnRootPrepareError func(error) error
		OnRootBefore       func(*Context) error
		OnRootAfter        func(*Context) error
		routersMap map[string]string
		parent   *Command
		children []*Command
		isServer bool
		locker     sync.Mutex  
		usage      string
		usageStyle UsageStyle
	}
	CommandTree struct {
		command *Command
		forest  []*CommandTree
	}
)
func (cmd *Command) Register(child *Command) *Command {
	if child == nil {
		panic("command `" + cmd.Name + "` try register a nil command")
	}
	if !IsValidCommandName(child.Name) {
		panic("illegal command name `" + cmd.Name + "`")
	}
	if cmd.children == nil {
		cmd.children = []*Command{}
	}
	if child.parent != nil {
		panic("command `" + child.Name + "` has been child of `" + child.parent.Name + "`")
	}
	if cmd.findChild(child.Name) != nil {
		panic("repeat register child `" + child.Name + "` for command `" + cmd.Name + "`")
	}
	if child.Aliases != nil {
		for _, alias := range child.Aliases {
			if cmd.findChild(alias) != nil {
				panic("repeat register child `" + alias + "` for command `" + cmd.Name + "`")
			}
		}
	}
	cmd.children = append(cmd.children, child)
	child.parent = cmd
	return child
}
func (cmd *Command) RegisterFunc(name string, fn CommandFunc, argvFn ArgvFunc) *Command {
	return cmd.Register(&Command{Name: name, Fn: fn, Argv: argvFn})
}
func (cmd *Command) RegisterTree(forest ...*CommandTree) {
	for _, tree := range forest {
		cmd.Register(tree.command)
		if tree.forest != nil && len(tree.forest) > 0 {
			tree.command.RegisterTree(tree.forest...)
		}
	}
}
func (cmd *Command) Parent() *Command {
	return cmd.parent
}
func (cmd *Command) IsServer() bool {
	return cmd.isServer
}
func (cmd *Command) IsClient() bool {
	return !cmd.IsServer()
}
func (cmd *Command) SetIsServer(yes bool) {
	cmd.Root().isServer = yes
}
func (cmd *Command) Run(args []string) error {
	return cmd.RunWith(args, nil, nil)
}
func (cmd *Command) RunWith(args []string, writer io.Writer, resp http.ResponseWriter, httpMethods ...string) error {
	fds := []uintptr{}
	if writer == nil {
		writer = colorable.NewColorableStdout()
		fds = append(fds, os.Stdout.Fd())
	}
	clr := color.Color{}
	colorSwitch(&clr, writer, fds...)
	var ctx *Context
	var suggestion string
	ctx, suggestion, err := cmd.prepare(clr, args, writer, resp, httpMethods...)
	if err == ExitError {
		return nil
	}
	if err != nil {
		if cmd.OnRootPrepareError != nil {
			err = cmd.OnRootPrepareError(err)
		}
		if err != nil {
			return wrapErr(err, suggestion, clr)
		}
		return nil
	}
	if ctx.command.NoHook {
		return ctx.command.Fn(ctx)
	}
	funcs := []func(*Context) error{
		ctx.command.OnBefore,
		cmd.OnRootBefore,
		ctx.command.Fn,
		cmd.OnRootAfter,
		ctx.command.OnAfter,
	}
	for _, f := range funcs {
		if f != nil {
			if err := f(ctx); err != nil {
				if err == ExitError {
					return nil
				}
				return err
			}
		}
	}
	return nil
}
func isEmptyArgvList(argvList []interface{}) bool {
	if argvList == nil {
		return true
	}
	for _, argv := range argvList {
		if argv != nil {
			return false
		}
	}
	return true
}
func (cmd *Command) argvList() []interface{} {
	argvList := make([]interface{}, 0, 1)
	if cmd.Argv != nil {
		argvList = append(argvList, cmd.Argv())
	} else {
		argvList = append(argvList, nil)
	}
	next := cmd.parent
	for next != nil {
		if next.Argv != nil && next.Global {
			argvList = append(argvList, next.Argv())
		} else {
			argvList = append(argvList, nil)
		}
		next = next.parent
	}
	return argvList
}
func (cmd *Command) prepare(clr color.Color, args []string, writer io.Writer, resp http.ResponseWriter, httpMethods ...string) (ctx *Context, suggestion string, err error) {
	router := []string{}
	for _, arg := range args {
		if strings.HasPrefix(arg, dashOne) {
			break
		}
		router = append(router, arg)
	}
	path := strings.Join(router, " ")
	child, end := cmd.SubRoute(router)
	if len(child.children) > 0 && !child.CanSubRoute && end != len(router) {
		suggestions := cmd.Suggestions(path)
		buff := bytes.NewBufferString("")
		if suggestions != nil && len(suggestions) > 0 {
			if len(suggestions) == 1 {
				fmt.Fprintf(buff, "\nDid you mean %s?", clr.Bold(suggestions[0]))
			} else {
				fmt.Fprintf(buff, "\n\nDid you mean one of these?\n")
				for _, sug := range suggestions {
					fmt.Fprintf(buff, "    %s\n", sug)
				}
			}
		}
		suggestion = buff.String()
		err = throwCommandNotFound(clr.Yellow(path))
		return
	}
	methodAllowed := false
	if len(httpMethods) == 0 ||
		child.HTTPMethods == nil ||
		len(child.HTTPMethods) == 0 {
		methodAllowed = true
	} else {
		method := httpMethods[0]
		for _, m := range child.HTTPMethods {
			if method == m {
				methodAllowed = true
				break
			}
		}
	}
	if !methodAllowed {
		err = throwMethodNotAllowed(clr.Yellow(httpMethods[0]))
		return
	}
	argvList := child.argvList()
	path = child.Path()
	ctx, err = newContext(path, router[:end], args[end:], argvList, clr)
	ctx.command = child
	ctx.writer = writer
	if !ctx.flagSet.hasForce {
		if !child.checkNumOption(ctx.NOpt()) || !ctx.command.checkNumArg(ctx.NArg()) {
			ctx.WriteUsage()
			err = ExitError
			return
		}
	}
	if err != nil {
		return
	}
	ctx.HTTPResponse = resp
	for _, argv := range argvList {
		if argv != nil {
			if helper, ok := argv.(AutoHelper); ok && helper.AutoHelp() {
				ctx.WriteUsage()
				err = ExitError
				return
			}
		}
	}
	if len(router) == 0 && cmd.Fn == nil {
		err = throwCommandNotFound(clr.Yellow(cmd.Name))
		return
	}
	if !ctx.flagSet.hasForce {
		for _, argv := range argvList {
			if argv != nil {
				if validator, ok := argv.(Validator); ok {
					err = validator.Validate(ctx)
					if err != nil {
						return
					}
				}
			}
		}
	}
	return
}
func (cmd *Command) checkNumArg(num int) bool {
	return cmd.NumArg == nil || cmd.NumArg(num)
}
func (cmd *Command) checkNumOption(num int) bool {
	return cmd.NumOption == nil || cmd.NumOption(num)
}
func (cmd *Command) Usage(ctx *Context) string {
	if cmd.UsageFn != nil {
		return cmd.UsageFn()
	}
	return cmd.defaultUsageFn(ctx)
}
func (cmd *Command) defaultUsageFn(ctx *Context) string {
	var (
		style = GetUsageStyle()
		clr   = *(ctx.Color())
	)
	cmd.locker.Lock()
	tmpUsage := cmd.usage
	usageStyle := cmd.usageStyle
	cmd.locker.Unlock()
	if tmpUsage != "" && usageStyle == style {
		return tmpUsage
	}
	buff := bytes.NewBufferString("")
	if cmd.Desc != "" {
		fmt.Fprintf(buff, "%s\n\n", cmd.Desc)
	}
	if cmd.Text != "" {
		fmt.Fprintf(buff, "%s\n\n", cmd.Text)
	}
	argvList := cmd.argvList()
	isEmpty := isEmptyArgvList(argvList)
	if !isEmpty {
		fmt.Fprintf(buff, "%s:\n\n%s", clr.Bold("Options"), usage(argvList, clr, style))
	}
	if cmd.children != nil && len(cmd.children) > 0 {
		if !isEmpty {
			buff.WriteByte('\n')
		}
		fmt.Fprintf(buff, "%s:\n\n%v", clr.Bold("Commands"), cmd.ChildrenDescriptions("  ", "   "))
	}
	tmpUsage = buff.String()
	cmd.locker.Lock()
	cmd.usage = tmpUsage
	cmd.usageStyle = style
	cmd.locker.Unlock()
	return tmpUsage
}