Documentation
¶
Overview ¶
The cli package provides a simple library for creating command-line applications. See http://github.com/turbinelabs/cli for a richer discussion of motivation and feature set. See the examples for basic usage.
Example (SingleCommand) ¶
This example shows how to create a single-command CLI
// This package contains a trivial example use of the cli package
package main
import (
"fmt"
"strconv"
"github.com/turbinelabs/cli"
"github.com/turbinelabs/cli/command"
"github.com/turbinelabs/nonstdlib/flag/usage"
)
// The typical pattern is to provide a public Cmd() func. This function should
// initialize the command.Cmd, the command.Runner, and flags.
func Cmd() *command.Cmd {
// typically the command.Runner is initialized only with internally-defined
// state; all necessary external state should be provided via flags. One can
// inline the initializaton of the command.Runner in the command.Cmd
// initialization if no flags are necessary, but it's often convenient to
// have a typed reference
runner := &runner{}
cmd := &command.Cmd{
Name: "adder",
Summary: "add a delimited string of integers together",
Usage: "[OPTIONS] <int>...",
Description: "add a delimited string of integers together",
Runner: runner,
}
// The flag.FlagSet is a member of the command.Cmd, and the flag
// value is a member of the command.Runner.
cmd.Flags.BoolVar(&runner.verbose, "verbose", false, "Produce verbose output")
// If we wrap flag.Required(...) around the usage string, Cmd.Run(...)
// will fail if it is unspecified
cmd.Flags.StringVar(&runner.thing, "thing", "", usage.Required("The thing"))
return cmd
}
// The private command.Runner implementation should contain any state needed
// to execute the command. The values should be initialized via flags declared
// in the Cmd() function.
type runner struct {
verbose bool
thing string
}
// Run does the actual work, based on state provided by flags, and the
// args remaining after the flags have been parsed.
func (f *runner) Run(cmd *command.Cmd, args []string) command.CmdErr {
ints := []int{}
sum := 0
// argument validation should occur at the top of the function, and
// errors should be reported via the cmd.BadInput or cmd.BadInputf methods.
// In this case, the main work of the function is done at the same time.
for _, arg := range args {
i, err := strconv.Atoi(arg)
if err != nil {
return cmd.BadInputf("Bad integer: \"%s\": %s", arg, err)
}
ints = append(ints, i)
sum += i
}
if f.verbose && len(ints) > 0 {
fmt.Print(ints[0])
for _, i := range ints[1:] {
fmt.Print(" + ", i)
}
fmt.Print(" = ")
}
fmt.Printf(`The thing: %s, the sum: %d`, f.thing, sum)
// In this case, there was no error. Errors should be returned via the
// cmd.Error or cmd.Errorf methods.
return command.NoError()
}
func mkSingleCmdCLI() cli.CLI {
// make a new CLI passing the version string and a command.Cmd
// while it's possible to add flags to the CLI, they are ignored; only the
// Cmd's flags are presented to the user.
return cli.New("1.0.2", Cmd())
}
// This example shows how to create a single-command CLI
func main() {
// this would be your main() function
// run the Main function, which calls os.Exit with the appropriate exit status
mkSingleCmdCLI().Main()
}
// Add the following to your tests to validate that there are no collisions
// between command flags and that help text can be generated without error:
// package main
// import (
// "testing"
// "github.com/turbinelabs/test/assert"
// )
// func TestCLI(t *testing.T) {
// assert.Nil(t, mkCLI().Validate())
// }
Example (SubCommands) ¶
This example shows how to create a CLI with multiple sub-commands
// This package contains a trivial example use of the cli package
package main
import (
"fmt"
"strings"
"github.com/turbinelabs/cli"
"github.com/turbinelabs/cli/command"
)
// The typical pattern is to provide a public CmdXYZ() func for each
// sub-command you wish to provide. This function should initialize the
// command.Cmd, the command.Runner, and flags.
func CmdSplit() *command.Cmd {
// typically the command.Runner is initialized only with internally-defined
// state; all necessary external state should be provided via flags. One can
// inline the initializaton of the command.Runner in the command.Cmd
// initialization if no flags are necessary, but it's often convenient to
// have a typed reference
runner := &splitRunner{}
cmd := &command.Cmd{
Name: "split",
Summary: "split strings",
Usage: "[OPTIONS] <string>",
Description: "split strings using the specified delimiter",
Runner: runner,
}
// The flag.FlagSet is a member of the command.Cmd, and the flag
// value is a member of the command.Runner.
cmd.Flags.StringVar(&runner.delim, "delim", ",", "The delimiter on which to split the string")
return cmd
}
// The private command.Runner implementation should contain any state needed
// to execute the command. The values should be initialized via flags declared
// in the CmdXYZ() function.
type splitRunner struct {
delim string
}
// Run does the actual work, based on state provided by flags, and the
// args remaining after the flags have been parsed.
func (f *splitRunner) Run(cmd *command.Cmd, args []string) command.CmdErr {
// argument validation should occur at the top of the function, and
// errors should be reported via the cmd.BadInput or cmd.BadInputf methods
if len(args) < 1 {
return cmd.BadInput("missing \"string\" argument.")
}
str := args[0]
if globalFlags.verbose {
fmt.Printf("Splitting \"%s\"\n", str)
}
split := strings.Split(str, f.delim)
for i, term := range split {
if globalFlags.verbose {
fmt.Printf("[%d] ", i)
}
fmt.Println(term)
}
// In this case, there was no error. Errors should be returned via the
// cmd.Error or cmd.Errorf methods.
return command.NoError()
}
// A second command
func CmdJoin() *command.Cmd {
runner := &joinRunner{}
cmd := &command.Cmd{
Name: "join",
Summary: "join strings",
Usage: "[OPTIONS] <string>...",
Description: "join strings using the specified delimiter",
Runner: runner,
}
cmd.Flags.StringVar(&runner.delim, "delim", ",", "The delimiter with which to join the strings")
return cmd
}
// a second Runner
type joinRunner struct {
delim string
}
func (f *joinRunner) Run(cmd *command.Cmd, args []string) command.CmdErr {
if globalFlags.verbose {
fmt.Printf("Joining \"%v\"\n", args)
}
joined := strings.Join(args, f.delim)
fmt.Println(joined)
return command.NoError()
}
// while not manditory, keeping globally-configured flags in a single struct
// makes it obvious where they came from at access time.
type globalFlagsT struct {
verbose bool
}
var globalFlags = globalFlagsT{}
func mkSubCmdCLI() cli.CLI {
// make a new CLI passing the description and version and one or more sub commands
c := cli.NewWithSubCmds(
"an example CLI for simple string operations",
"1.2.3",
CmdSplit(),
CmdJoin(),
)
// Global flags can be used to modify global state
c.Flags().BoolVar(&globalFlags.verbose, "verbose", false, "Produce verbose output")
return c
}
// This example shows how to create a CLI with multiple sub-commands
func main() {
// this would be your main() function
// run the Main function, which calls os.Exit with the appropriate exit status
mkSubCmdCLI().Main()
}
// Add the following to your tests to validate that there are no collisions
// between command flags:
// package main
// import (
// "testing"
// "github.com/turbinelabs/test/assert"
// )
// func TestCLI(t *testing.T) {
// assert.Nil(t, mkCLI().Validate())
// }
Index ¶
Examples ¶
Constants ¶
View Source
const HelpSummary = "Show a list of commands or help for one command"
View Source
const VersionSummary = "Print the version and exit"
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CLI ¶
type CLI interface {
// Flags returns a pointer to the global flags for the CLI
Flags() *flag.FlagSet
// Set the flags
SetFlags(*flag.FlagSet)
// Main serves as the main() function for the CLI. It will parse
// the command-line arguments and flags, call the appropriate sub-command,
// and return exit status and output error messages as appropriate.
Main()
// Validate can be used to make sure the CLI is well-defined from within
// unit tests. In particular it will validate that no two flags exist with
// the same environment key. As a last-ditch effort, Validate will be called
// at the start of Main. ValidationFlag values may be passed to alter the
// level of validation performed.
Validate(...ValidationFlag) error
// Returns the CLI version data.
Version() app.Version
}
A CLI represents a command-line application
type ValidationFlag ¶
type ValidationFlag int
const ( // Skips Validating that global and subcommand help text can // be generated. ValidateSkipHelpText ValidationFlag = iota )
Directories
¶
| Path | Synopsis |
|---|---|
|
The app package provides a simple App struct to describe a command-line application, and a Usage interface, which describers the global and command-specific usage of the App.
|
The app package provides a simple App struct to describe a command-line application, and a Usage interface, which describers the global and command-specific usage of the App. |
|
The command package provides an abstraction for a command-line application sub-command, a means to execute code when that sub-command is invoked, a means to report success/failure status of said code, and generic implementations of help and version sub-commands.
|
The command package provides an abstraction for a command-line application sub-command, a means to execute code when that sub-command is invoked, a means to report success/failure status of said code, and generic implementations of help and version sub-commands. |
Click to show internal directories.
Click to hide internal directories.