1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
// Package cli handles command line arguments.
package cli
import (
"flag"
"fmt"
"os"
)
// NewProgram makes a new Program instance.
func NewProgram(name string, version string) *Program {
return &Program{map[string]Commander{}, name, version, false, false, false, false}
}
// NewCommand makes a new Program instance.
func NewCommand(name string, desc string, fn func(s Commander) error) *Command {
set := flag.NewFlagSet(name, flag.ExitOnError)
set.SetOutput(os.Stdout)
return &Command{name, desc, set, fn}
}
// Program is a struct to define a program and its command.
type Program struct {
commands map[string]Commander
ProgramName string
ProgramVersion string
Help bool
ShortHelp bool
Version bool
ShortVersion bool
}
// Bind help and version flag.
func (p *Program) Bind() {
flag.CommandLine.Init(p.ProgramName, flag.ExitOnError)
flag.CommandLine.SetOutput(os.Stdout)
flag.BoolVar(&p.ShortHelp, "h", false, "Show help")
flag.BoolVar(&p.Help, "help", false, "Show help")
flag.BoolVar(&p.Version, "version", false, "Show version")
flag.BoolVar(&p.ShortVersion, "v", false, "Show version")
}
// Add a new sub command.
func (p *Program) Add(c Commander) bool {
p.commands[c.getName()] = c
return true
}
// ShowVersion prints program name and version on stderr.
func (p *Program) ShowVersion() error {
fmt.Fprintf(os.Stdout, "%s - %v\n", p.ProgramName, p.ProgramVersion)
return nil
}
// ShowUsage prints program usage of given subCmd on stderr. If subCmd is empty, prints general usage.
func (p *Program) ShowUsage(subCmd string) error {
if subCmd == "" {
p.ShowVersion()
fmt.Fprintln(os.Stdout, "\nUsage")
flag.PrintDefaults()
fmt.Fprintln(os.Stdout, "\nCommands")
for name, c := range p.commands {
fmt.Fprintf(os.Stderr, "\t%v\t%v\n", name, c.getDesc())
}
return nil
}
if cmd := p.commands[subCmd]; cmd != nil {
return p.ShowCmdUsage(cmd)
}
return fmt.Errorf("No such command %q", subCmd)
}
// ShowCmdUsage prints command usage of given subCmd on stderr.
func (p *Program) ShowCmdUsage(cmd Commander) error {
p.ShowVersion()
fmt.Fprintf(os.Stdout, "\nCommand %q: %v\n", cmd.getName(), cmd.getDesc())
cmd.getSet().PrintDefaults()
return nil
}
// Run the program against given set of arguments.
func (p *Program) Run(args []string) error {
if len(args) > 1 {
if cmd, ok := p.commands[args[1]]; ok {
if err := cmd.getSet().Parse(args[2:]); err != nil {
return nil
}
} else {
flag.Parse()
}
for name, cmd := range p.commands {
if cmd.getSet().Parsed() {
err := cmd.getFn()(cmd)
if err != nil {
err = fmt.Errorf("command %q failed: %v", name, err)
}
return err
}
}
}
if p.Version || p.ShortVersion {
return p.ShowVersion()
}
return p.ShowUsage("")
}
// Commander is an generalizer of Command.
type Commander interface {
getDesc() string
getName() string
getSet() *flag.FlagSet
getFn() func(s Commander) error
}
// Command describe a program sub command.
type Command struct {
name string
desc string
Set *flag.FlagSet
fn func(s Commander) error
}
func (c *Command) getDesc() string {
return c.desc
}
func (c *Command) getName() string {
return c.name
}
func (c *Command) getSet() *flag.FlagSet {
return c.Set
}
func (c *Command) getFn() func(s Commander) error {
return c.fn
}
|