aboutsummaryrefslogtreecommitdiff
path: root/cmd/cmd.go
diff options
context:
space:
mode:
authorJoseph Richey <joerichey94@gmail.com>2017-10-16 10:50:53 -0700
committerJoseph Richey <joerichey94@gmail.com>2017-10-19 02:22:25 -0700
commitb5cc60b2b974645f0d09721c292cd243d049cbcf (patch)
treefbc44d87ba8dd83e2238ff8e217ea560f56b3091 /cmd/cmd.go
parentb4299090c3e503ba0c49a6086b1a46c218ca45f4 (diff)
Refactor almost complete
Diffstat (limited to 'cmd/cmd.go')
-rw-r--r--cmd/cmd.go209
1 files changed, 95 insertions, 114 deletions
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 945e945..3358015 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -1,5 +1,5 @@
/*
- * cmd.go - Main interface to cmd package (running, Cmd and Flag structs, etc)
+ * cmd.go - Main interface to cmd package (Context, Command, Flag, etc...)
*
* Copyright 2017 Google Inc.
* Author: Joe Richey (joerichey@google.com)
@@ -17,34 +17,93 @@
* the License.
*/
-// Package cmd is the common library for writing command line binaries.
-// This package is mainly a wrapper around github.com/urfave/cli, but provides
-// additional support to make the usage look similar to the man page.
-//
-// The main componets are the `Cmd`, `Argument`, and `Flag` types which can be
-// used to define a top-level command with many potential subcommands. This
-// package also presents a smaller interface than urfave/cli, making it easier
-// to use for other commands.
+// Package cmd is the common library for writing command line binaries. The main
+// componets are the `Command`, `Context`, `Argument`, and `Flag` types which
+// can be used to define a top-level command with many potential subcommands.
package cmd
import (
"fmt"
- "io"
"os"
- "text/template"
- "time"
+ "path/filepath"
- "github.com/blang/semver"
+ "github.com/pkg/errors"
)
+// Command represents a command with many potential sub-commands.
+type Command struct {
+ Name string
+ Title string
+ UsageLines []string
+ SubCommands []*Command
+ InheritArguments bool
+ Arguments []*Argument
+ InheritFlags bool
+ Flags []Flag
+ ManPage *ManPage
+ Action Action
+}
+
+// Run executes the command with os.Args, equivalent to c.RunArgs(os.Args).
+func (c *Command) Run() {
+ c.RunArgs(os.Args)
+}
+
+// RunArgs executes the command with the provided args. If the Name argument is
+// empty, args[0]'s basename is used instead. If the command fails, this method
+// will not return.
+func (c *Command) RunArgs(args []string) {
+ binaryPath, args := args[0], args[1:]
+ if c.Name == "" {
+ c.Name = filepath.Base(binaryPath)
+ }
+
+ // Create our initial context by sorting the arguments.
+ ctx := &Context{Command: c}
+ ctx.Args, ctx.flagArgs = sortArgs(args)
+
+ ctx.run()
+}
+
+// Action 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 Action func(ctx *Context) error
+
// Context represents the state of a running application, and is the only thing
-// passed to a CommandFunc.
+// passed to an Action.
type Context struct {
- Command *Command
- Parent *Context
- Info *Info
- Args []string
+ // The current command being executed
+ Command *Command
+ // The context of the parent command before this command was executed.
+ // Nil if this is the root context.
+ Parent *Context
+ // The non-flag arguments being passed to the command.
+ Args []string
+ // The flag arguments being passed to the command.
flagArgs []string
+ // The mapping of error causes to help strings
+ errorMap map[error]string
+}
+
+// FullArguments returns the list of arguments for the current command and its
+// parent arguments (if InheritArguments) is true.
+func (ctx *Context) FullArguments() []*Argument {
+ if ctx.Parent == nil || !ctx.Command.InheritArguments {
+ return ctx.Command.Arguments
+ }
+ return append(ctx.Command.Arguments, ctx.Parent.FullArguments()...)
+}
+
+// FullFlags returns the list of flags for the current command and its parent
+// arguments (if InheritFlags) is true.
+func (ctx *Context) FullFlags() []Flag {
+ if ctx.Parent == nil || !ctx.Command.InheritFlags {
+ return ctx.Command.Flags
+ }
+ return append(ctx.Command.Flags, ctx.Parent.FullFlags()...)
}
// FullName returns the space-separated name of the command and all parents.
@@ -55,41 +114,30 @@ func (ctx *Context) FullName() string {
return fmt.Sprintf("%s %s", ctx.Parent.FullName(), ctx.Command.Name)
}
+// Info returns the same information as cmd.Info. This method only exists so
+// that Info can be accessed in an output template.
+func (*Context) Info() *InfoData {
+ return Info
+}
+
// 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 {
+ if ctx.Parent == nil || ctx.Command.ManPage != 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)
+// getHelp tries to find a helpMap and then lookup the error by it's cause.
+func (ctx *Context) getHelp(err error) string {
+ if ctx.errorMap != nil {
+ return ctx.errorMap[errors.Cause(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
+ if ctx.Parent == nil {
+ return ""
+ }
+ return ctx.Parent.getHelp(err)
}
// Argument represents a parameter passed to a function. It has an optional
@@ -101,77 +149,10 @@ type Argument struct {
func (a *Argument) String() string { return fmt.Sprintf("<%s>", a.ArgName) }
-// ManPage a man page with a title and section.
+// ManPage a man page with a name and section.
type ManPage struct {
- Title string
+ Name string
Section int
}
-// 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() {
- c.RunArgs(os.Args)
-}
-
-// RunArgs executes the command with the provided args. If the Name argument is
-// empty, args[0]'s basename is used instead. If the command fails, this method
-// will not return.
-func (c *Command) RunArgs(args []string) {
- 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
-}
+func (m *ManPage) String() string { return fmt.Sprintf("%s(%d)", m.Name, m.Section) }