diff options
| author | Joe Richey joerichey@google.com <joerichey@google.com> | 2017-10-12 17:59:45 -0700 |
|---|---|---|
| committer | Joseph Richey <joerichey94@gmail.com> | 2017-10-19 02:22:25 -0700 |
| commit | b4299090c3e503ba0c49a6086b1a46c218ca45f4 (patch) | |
| tree | 889adbf3da9616a5c6eaa783291e5f94c01955a2 /cmd/cmd.go | |
| parent | 921f1c977c4e0704f61e3a7c092d3a4317ab278c (diff) | |
Command, Context, command line splitting setup
Diffstat (limited to 'cmd/cmd.go')
| -rw-r--r-- | cmd/cmd.go | 146 |
1 files changed, 124 insertions, 22 deletions
@@ -27,18 +27,69 @@ // to use for other commands. package cmd -import "os" +import ( + "fmt" + "io" + "os" + "text/template" + "time" -// Command represents a command with many potential top-level commands. This is -// transformed into a cli.Command in Run(). -type Command struct { - Name string - UsageLines []string - SubCmds []*Command - Arguments []*Argument - Flags []Flag - ManPage *ManEntry - Action CommandFunc + "github.com/blang/semver" +) + +// Context represents the state of a running application, and is the only thing +// passed to a CommandFunc. +type Context struct { + Command *Command + Parent *Context + Info *Info + Args []string + flagArgs []string +} + +// FullName returns the space-separated name of the command and all parents. +func (ctx *Context) FullName() string { + if ctx.Parent == nil { + return ctx.Command.Name + } + return fmt.Sprintf("%s %s", ctx.Parent.FullName(), ctx.Command.Name) +} + +// ManPage returns the man page entry for this context. It is either the ManPage +// for the the current command or the closet Parent. +func (ctx *Context) ManPage() *ManPage { + if ctx.Command.ManPage.Section != 0 || ctx.Parent == nil { + return ctx.Command.ManPage + } + return ctx.Parent.ManPage() +} + +// Creates an anonymous template from the text, and runs it with the provided +// Context and writer. Panics if text has a bad format or execution fails. +func (ctx *Context) executeTemplate(w io.Writer, text string) { + tmpl := template.Must(template.New("").Parse(text)) + if err := tmpl.Execute(w, ctx); err != nil { + panic(err) + } +} + +func (ctx *Context) execute() { + fmt.Printf("%+v\n", ctx) + return +} + +// Info is a parsed view of the corresponding global variables. +type Info struct { + Version semver.Version + BuildTime time.Time + Authors []Author + Copyright string +} + +// Author contains the contact information for a contributor. +type Author struct { + Name string + Email string } // Argument represents a parameter passed to a function. It has an optional @@ -48,19 +99,35 @@ type Argument struct { Usage string } -// ManEntry represents an entry in a man page with a name, section, and title. -type ManEntry struct { - Name string - Section int +func (a *Argument) String() string { return fmt.Sprintf("<%s>", a.ArgName) } + +// ManPage a man page with a title and section. +type ManPage struct { Title string + Section int } -// CommandFunc contains the implementation of a command. The provided args have -// the flags and leading command names removed. If a normal error is returned, -// it is printed out (with an optional explanation) and exits with FailureCode. -// If a usage error is returned, it is printed out with the command's usage and -// exits with UsageFailureCode. Returning nil causes an exit with success. -type CommandFunc func(args []string) error +// CommandFunc contains the implementation of a command. If a normal error is +// returned, the error will be printed out (with an optional explanation) and +// Run will exit with FailureCode. If a usage error is returned, the error and +// the commnd's usage are printed out and Run will exit with UsageFailureCode. +// Returning nil causes Run to return. +type CommandFunc func(ctx *Context) error + +// Command represents a command with many potential top-level commands. This is +// transformed into a cli.Command in Run(). +type Command struct { + Name string + Title string + UsageLines []string + SubCommands []*Command + InheritArguments bool + Arguments []*Argument + InheritFlags bool + Flags []Flag + ManPage *ManPage + Action CommandFunc +} // Run executes the command with os.Args, equivalent to c.RunArgs(os.Args). func (c *Command) Run() { @@ -71,5 +138,40 @@ func (c *Command) Run() { // empty, args[0]'s basename is used instead. If the command fails, this method // will not return. func (c *Command) RunArgs(args []string) { - // TODO(joerichey): Implement conversion to cli.Command + binaryName, args := args[0], args[1:] + if c.Name == "" { + c.Name = binaryName + } + + // Create our initial context by sorting the args and parsing the tags. + ctx := &Context{ + Command: c, + Info: parseInfo(), + } + ctx.Args, ctx.flagArgs = sortArgs(args) + + ctx.execute() +} + +// Divide the arguments into flag arguments (those starting with "-") and normal +// arguments. If "--" appears in the list, it will classified as a normal +// argument as well as all arguments following it. Also removes empty args. +func sortArgs(args []string) (normalArgs, flagArgs []string) { + var arg string + for len(args) > 0 { + arg, args = args[0], args[1:] + if arg == "" { + continue + } + if arg == "--" { + normalArgs = append(normalArgs, arg) + normalArgs = append(normalArgs, args...) + return + } else if arg[0] == '-' { + flagArgs = append(flagArgs, arg) + } else { + normalArgs = append(normalArgs, arg) + } + } + return } |